Guide to the Secure Configuration of Red Hat Enterprise Linux 8

with profile DISA STIG for Red Hat Enterprise Linux 8
This profile contains configuration checks that align to the DISA STIG for Red Hat Enterprise Linux 8 V2R4. In addition to being applicable to Red Hat Enterprise Linux 8, this configuration baseline is applicable to the operating system tier of Red Hat technologies that are based on Red Hat Enterprise Linux 8, such as: - Red Hat Enterprise Linux Server - Red Hat Enterprise Linux Workstation and Desktop - Red Hat Enterprise Linux for HPC - Red Hat Storage - Red Hat Containers with a Red Hat Enterprise Linux 8 image
This guide presents a catalog of security-relevant configuration settings for Red Hat Enterprise Linux 8. It is a rendering of content structured in the eXtensible Configuration Checklist Description Format (XCCDF) in order to support security automation. The SCAP content is is available in the scap-security-guide package which is developed at https://www.open-scap.org/security-policies/scap-security-guide.

Providing system administrators with such guidance informs them how to securely configure systems under their control in a variety of network roles. Policy makers and baseline creators can use this catalog of settings, with its associated references to higher-level security control catalogs, in order to assist them in security baseline creation. This guide is a catalog, not a checklist, and satisfaction of every item is not likely to be possible or sensible in many operational scenarios. However, the XCCDF format enables granular selection and adjustment of settings, and their association with OVAL and OCIL content provides an automated checking capability. Transformations of this document, and its associated automated checking content, are capable of providing baselines that meet a diverse set of policy objectives. Some example XCCDF Profiles, which are selections of items that form checklists and can be used as baselines, are available with this guide. They can be processed, in an automated fashion, with tools that support the Security Content Automation Protocol (SCAP). The DISA STIG, which provides required settings for US Department of Defense systems, is one example of a baseline created from this guidance.
Do not attempt to implement any of the settings in this guide without first testing them in a non-operational environment. The creators of this guidance assume no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic.

Evaluation Characteristics

Evaluation targetlocalhost
Benchmark URL#scap_org.open-scap_comp_ssg-rhel8-xccdf.xml
Benchmark IDxccdf_org.ssgproject.content_benchmark_RHEL-8
Benchmark version0.1.78
Profile IDxccdf_org.ssgproject.content_profile_stig
Started at2025-11-18T17:04:59-05:00
Finished at2025-11-18T17:07:35-05:00
Performed byadmin
Test systemcpe:/a:redhat:openscap:1.3.12

CPE Platforms

  • cpe:/o:redhat:enterprise_linux:8.10
  • cpe:/o:redhat:enterprise_linux:8
  • cpe:/o:redhat:enterprise_linux:8.0
  • cpe:/o:redhat:enterprise_linux:8.1
  • cpe:/o:redhat:enterprise_linux:8.2
  • cpe:/o:redhat:enterprise_linux:8.3
  • cpe:/o:redhat:enterprise_linux:8.4
  • cpe:/o:redhat:enterprise_linux:8.5
  • cpe:/o:redhat:enterprise_linux:8.6
  • cpe:/o:redhat:enterprise_linux:8.7
  • cpe:/o:redhat:enterprise_linux:8.8
  • cpe:/o:redhat:enterprise_linux:8.9

Addresses

  • IPv4  127.0.0.1
  • IPv4  192.168.1.230
  • IPv4  192.168.122.1
  • IPv6  0:0:0:0:0:0:0:1
  • IPv6  fe80:0:0:0:be24:11ff:fe1e:c806
  • MAC  00:00:00:00:00:00
  • MAC  BC:24:11:1E:C8:06
  • MAC  52:54:00:B6:A1:66

Compliance and Scoring

The target system did not satisfy the conditions of 252 rules! Furthermore, the results of 1 rules were inconclusive. Please review rule results and consider applying remediation.

Rule results

114 passed
252 failed
11 other

Severity of failed rules

0 other
22 low
215 medium
15 high

Score

Scoring systemScoreMaximumPercent
urn:xccdf:scoring:default44.510071100.000000
44.51%

Rule Overview

Group rules by:
TitleSeverityResult
Guide to the Secure Configuration of Red Hat Enterprise Linux 8 252x fail 1x error 10x notchecked
System Settings 142x fail 1x error 7x notchecked
Installing and Maintaining Software 39x fail 2x notchecked
System and Software Integrity 15x fail
Software Integrity Checking 6x fail
Verify Integrity with AIDE 6x fail
Install AIDEmedium
fail
Build and Test AIDE Databasemedium
fail
Configure AIDE to Verify the Audit Toolsmedium
fail
Configure Notification of Post-AIDE Scan Detailsmedium
fail
Configure AIDE to Verify Access Control Lists (ACLs)low
fail
Configure AIDE to Verify Extended Attributeslow
fail
Audit Tools Must Be Group-owned by Rootmedium
pass
Audit Tools Must Be Owned by Rootmedium
pass
Audit Tools Must Have a Mode of 0755 or Less Permissivemedium
pass
Federal Information Processing Standard (FIPS) 4x fail
Enable Dracut FIPS Modulehigh
fail
Enable FIPS Modehigh
fail
FIPS Must Use a Supported Subpolicymedium
fail
Set kernel parameter 'crypto.fips_enabled' to 1high
fail
System Cryptographic Policies 5x fail
Configure BIND to use System Crypto Policyhigh
notapplicable
Configure System Cryptography Policyhigh
fail
Configure GnuTLS library to use DoD-approved TLS Encryptionmedium
pass
Configure Kerberos to use System Crypto Policyhigh
pass
Configure Libreswan to use System Crypto Policyhigh
pass
Configure OpenSSL library to use System Crypto Policymedium
pass
Configure OpenSSL library to use TLS Encryptionmedium
pass
Configure SSH to use System Crypto Policymedium
pass
Configure SSH Client to Use FIPS 140 Validated Ciphers: openssh.confighigh
fail
Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.configmedium
fail
Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.configmedium
fail
Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.configmedium
fail
Operating System Vendor Support and Certification
The Installed Operating System Is Vendor Supportedhigh
pass
Disk Partitioning 5x fail 1x notchecked
Encrypt Partitionshigh
notchecked
Ensure /home Located On Separate Partitionlow
pass
Ensure /tmp Located On Separate Partitionlow
fail
Ensure /var Located On Separate Partitionlow
fail
Ensure /var/log Located On Separate Partitionlow
fail
Ensure /var/log/audit Located On Separate Partitionlow
fail
Ensure /var/tmp Located On Separate Partitionmedium
fail
GNOME Desktop Environment 10x fail
Disable the GNOME3 Login User Listmedium
fail
Enable the GNOME3 Screen Locking On Smartcard Removalmedium
fail
Disable GDM Automatic Loginhigh
fail
Configure GNOME Screen Locking 6x fail
Set GNOME3 Screensaver Inactivity Timeoutmedium
fail
Set GNOME3 Screensaver Lock Delay After Activation Periodmedium
fail
Enable GNOME3 Screensaver Lock After Idle Periodmedium
fail
Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Periodmedium
fail
Ensure Users Cannot Change GNOME3 Screensaver Settingsmedium
fail
Ensure Users Cannot Change GNOME3 Session Idle Settingsmedium
fail
GNOME System Settings 1x fail
Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3high
fail
Sudo 2x fail
Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticatemedium
pass
Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWDmedium
pass
Require Re-Authentication When Using the sudo Commandmedium
fail
The operating system must restrict privilege elevation to authorized personnelmedium
pass
Ensure sudo only includes the default configuration directorymedium
pass
Ensure invoking users password for privilege escalation when using sudomedium
fail
System Tooling / Utilities 5x fail
Install rng-tools Packagelow
fail
Uninstall abrt-addon-ccpp Packagelow
pass
Uninstall abrt-addon-kerneloops Packagelow
pass
Uninstall abrt-cli Packagelow
pass
Uninstall abrt-plugin-sosreport Packagelow
pass
Uninstall gssproxy Packagemedium
fail
Uninstall iprutils Packagemedium
fail
Uninstall krb5-workstation Packagemedium
notapplicable
Uninstall libreport-plugin-logger Packagelow
pass
Uninstall libreport-plugin-rhtsupport Packagelow
fail
Uninstall python3-abrt-addon Packagelow
pass
Uninstall tuned Packagemedium
fail
Updating Software 2x fail 1x notchecked
Ensure yum Removes Previous Package Versionslow
pass
Ensure gpgcheck Is Enabled for All Package Repositorieshigh
fail
Ensure gpgcheck Enabled for Local Packageshigh
fail
Ensure Red Hat GPG Key Installedhigh
pass
Ensure Software Patches Installedmedium
notchecked
Account and Access Control 46x fail 1x error 2x notchecked
Warning Banners for System Accesses 3x fail
Enable GNOME3 Login Warning Bannermedium
fail
Set the GNOME3 Login Warning Banner Textmedium
fail
Modify the System Login Bannermedium
fail
Protect Accounts by Configuring PAM 22x fail
Set Lockouts for Failed Password Attempts 9x fail
Configure the Use of the pam_faillock.so Module in the /etc/pam.d/password-auth File.medium
fail
Configure the Use of the pam_faillock.so Module in the /etc/pam.d/system-auth File.medium
fail
Account Lockouts Must Be Loggedmedium
fail
Lock Accounts After Failed Password Attemptsmedium
fail
Configure the root Account for Failed Password Attemptsmedium
fail
Lock Accounts Must Persistmedium
fail
Set Interval For Counting Failed Password Attemptsmedium
fail
Do Not Show System Messages When Unsuccessful Logon Attempts Occurmedium
fail
Set Lockout Time for Failed Password Attemptsmedium
fail
Set Password Quality Requirements 11x fail
Set Password Quality Requirements with pam_pwquality 11x fail
Ensure PAM Enforces Password Requirements - Minimum Digit Charactersmedium
fail
Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Wordsmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Different Charactersmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Lowercase Charactersmedium
fail
Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Classmedium
fail
Set Password Maximum Consecutive Repeating Charactersmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Different Categoriesmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Lengthmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Special Charactersmedium
fail
Ensure PAM password complexity module is enabled in password-authmedium
pass
Ensure PAM password complexity module is enabled in system-authmedium
pass
Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Sessionmedium
fail
Ensure PAM Enforces Password Requirements - Minimum Uppercase Charactersmedium
fail
Set Password Hashing Algorithm 1x fail
Set Password Hashing Algorithm in /etc/login.defsmedium
pass
Set PAM''s Password Hashing Algorithm - password-authmedium
pass
Set PAM''s Password Hashing Algorithmmedium
pass
Set Password Hashing Rounds in /etc/login.defsmedium
fail
Disallow Configuration to Bypass Password Requirements for Privilege Escalationmedium
pass
Ensure PAM Displays Last Logon/Access Notificationlow
fail
Protect Physical Console Access 5x fail
Configure Screen Locking 1x fail
Install the opensc Package For Multifactor Authenticationmedium
fail
Install Smart Card Packages For Multifactor Authenticationmedium
pass
Disable debug-shell SystemD Servicemedium
fail
Disable Ctrl-Alt-Del Burst Actionhigh
fail
Disable Ctrl-Alt-Del Reboot Activationhigh
fail
Configure Logind to terminate idle sessions after certain time of inactivitymedium
fail
Require Authentication for Emergency Systemd Targetmedium
pass
Require Authentication for Single User Modemedium
pass
Protect Accounts by Restricting Password-Based Login 8x fail 1x notchecked
Set Account Expiration Following Inactivitymedium
fail
Set Password Expiration Parameters 5x fail
Set Password Maximum Agemedium
fail
Set Password Minimum Agemedium
fail
Set Password Minimum Length in login.defsmedium
fail
Set Existing Passwords Maximum Agemedium
fail
Set Existing Passwords Minimum Agemedium
fail
Verify Proper Storage and Existence of Password Hashes 1x fail
Verify All Account Password Hashes are Shadowed with SHA512medium
pass
Prevent Login to Accounts With Empty Passwordhigh
fail
Ensure There Are No Accounts With Blank or Null Passwordshigh
pass
Restrict Root Logins
Verify Only Root Has UID 0high
pass
Only Authorized Local User Accounts Exist on Operating Systemmedium
fail
Secure Session Configuration Files for Login Accounts 8x fail 1x error 1x notchecked
Ensure that Users Have Sensible Umask Values 4x fail 1x error
Ensure the Default Bash Umask is Set Correctlymedium
fail
Ensure the Default C Shell Umask is Set Correctlymedium
fail
Ensure the Default Umask is Set Correctly in login.defsmedium
fail
Ensure the Default Umask is Set Correctly in /etc/profilemedium
fail
Ensure the Default Umask is Set Correctly For Interactive Usersmedium
error
Ensure the Logon Failure Delay is Set Correctly in login.defsmedium
fail
Limit the Number of Concurrent Login Sessions Allowed Per Userlow
fail
User Initialization Files Must Not Run World-Writable Programsmedium
pass
Ensure that Users Path Contains Only Local Directoriesmedium
notchecked
All Interactive Users Must Have A Home Directory Definedmedium
pass
All Interactive Users Home Directories Must Existmedium
pass
All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary Groupmedium
pass
All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissivemedium
fail
All Interactive User Home Directories Must Be Group-Owned By The Primary Groupmedium
pass
Ensure All User Initialization Files Have Mode 0740 Or Less Permissivemedium
fail
All Interactive User Home Directories Must Have mode 0750 Or Less Permissivemedium
pass
Enable authselectmedium
pass
GRUB2 bootloader configuration 5x fail
Non-UEFI GRUB2 bootloader configuration 2x fail
Set the Boot Loader Admin Username to a Non-Default Valuehigh
fail
Set Boot Loader Password in grub2high
fail
UEFI GRUB2 bootloader configuration
Set the UEFI Boot Loader Admin Username to a Non-Default Valuemedium
notapplicable
Set the UEFI Boot Loader Passwordhigh
notapplicable
The system must booted with init_on_free=1medium
fail
Enable Kernel Page-Table Isolation (KPTI)low
fail
Disable vsyscallsmedium
fail
Configure Syslog 5x fail
Ensure Proper Configuration of Log Files 4x fail
Ensure cron Is Logging To Rsyslogmedium
pass
Ensure Rsyslog Authenticates Off-Loaded Audit Recordsmedium
fail
Ensure Rsyslog Encrypts Off-Loaded Audit Recordsmedium
fail
Ensure Rsyslog Encrypts Off-Loaded Audit Recordsmedium
fail
Ensure remote access methods are monitored in Rsyslogmedium
fail
Rsyslog Logs Sent To Remote Host 1x fail
Ensure Logs Sent To Remote Hostmedium
fail
Ensure rsyslog-gnutls is installedmedium
pass
Ensure rsyslog is Installedmedium
pass
Enable rsyslog Servicemedium
pass
Network Configuration and Firewalls 21x fail 2x notchecked
firewalld 1x fail 2x notchecked
Inspect and Activate Default firewalld Rules
Install firewalld Packagemedium
pass
Verify firewalld Enabledmedium
pass
Strengthen the Default Ruleset 1x fail 2x notchecked
Configure the Firewalld Portsmedium
notchecked
Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systemsmedium
notchecked
Set Default firewalld Zone for Incoming Packetsmedium
fail
Configure Firewalld to Use the Nftables Backendmedium
pass
IPv6 7x fail
Configure IPv6 Settings if Necessary 7x fail
Configure Accepting Router Advertisements on All IPv6 Interfacesmedium
fail
Disable Accepting ICMP Redirects for All IPv6 Interfacesmedium
fail
Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfacesmedium
fail
Disable Kernel Parameter for IPv6 Forwardingmedium
fail
Disable Accepting Router Advertisements on all IPv6 Interfaces by Defaultmedium
fail
Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfacesmedium
fail
Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Defaultmedium
fail
Kernel Parameters Which Affect Networking 7x fail
Network Related Kernel Runtime Parameters for Hosts and Routers 5x fail
Disable Accepting ICMP Redirects for All IPv4 Interfacesmedium
fail
Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfacesmedium
pass
Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfacesmedium
fail
Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfacesmedium
pass
Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfacesmedium
fail
Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Defaultmedium
fail
Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfacesmedium
fail
Network Parameters for Hosts Only 2x fail
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfacesmedium
fail
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Defaultmedium
fail
Uncommon Network Protocols 5x fail
Disable ATM Supportmedium
fail
Disable CAN Supportmedium
fail
Disable IEEE 1394 (FireWire) Supportlow
fail
Disable SCTP Supportmedium
fail
Disable TIPC Supportlow
fail
Wireless Networking 1x fail
Disable Wireless Through Software Configuration 1x fail
Disable Bluetooth Kernel Modulemedium
fail
Deactivate Wireless Network Interfacesmedium
notapplicable
Configure Multiple DNS Servers in /etc/resolv.confmedium
pass
Ensure System is Not Acting as a Network Sniffermedium
pass
File Permissions and Masks 25x fail
Verify Permissions on Important Files and Directories 1x fail
Verify Permissions on Files within /var/log Directory
Verify Group Who Owns /var/log Directorymedium
pass
Verify Group Who Owns /var/log/messages Filemedium
pass
Verify User Who Owns /var/log Directorymedium
pass
Verify User Who Owns /var/log/messages Filemedium
pass
Verify Permissions on /var/log Directorymedium
pass
Verify Permissions on /var/log/messages Filemedium
pass
Verify File Permissions Within Some Important Directories
Verify that Shared Library Directories Have Root Group Ownershipmedium
pass
Verify that Shared Library Directories Have Root Ownershipmedium
pass
Verify that Shared Library Directories Have Restrictive Permissionsmedium
pass
Verify that system commands files are group owned by root or a system accountmedium
pass
Verify that System Executables Have Root Ownershipmedium
pass
Verify that Shared Library Files Have Root Ownershipmedium
pass
Verify that System Executables Have Restrictive Permissionsmedium
pass
Verify that Shared Library Files Have Restrictive Permissionsmedium
pass
Verify the system-wide library files in directories "/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root.medium
pass
rootfiles 1x fail
Ensure rootfiles tmpfile.d is Configured Correctlymedium
fail
Ensure All World-Writable Directories Are Owned by root Usermedium
pass
Verify that All World-Writable Directories Have Sticky Bits Setmedium
pass
Ensure All World-Writable Directories Are Group Owned by a System Accountmedium
pass
Ensure All Files Are Owned by a Groupmedium
pass
Ensure All Files Are Owned by a Usermedium
pass
Restrict Dynamic Mounting and Unmounting of Filesystems 2x fail
Disable the Automountermedium
notapplicable
Disable Mounting of cramfslow
fail
Disable Modprobe Loading of USB Storage Drivermedium
fail
Restrict Partition Mount Options 7x fail
Add nosuid Option to /boot/efimedium
notapplicable
Add nosuid Option to /bootmedium
fail
Add nodev Option to /dev/shmmedium
fail
Add noexec Option to /dev/shmmedium
fail
Add nosuid Option to /dev/shmmedium
fail
Add noexec Option to /homemedium
fail
Add nosuid Option to /homemedium
fail
Add nodev Option to Non-Root Local Partitionsmedium
fail
Add nodev Option to Removable Media Partitionsmedium
pass
Add noexec Option to Removable Media Partitionsmedium
pass
Add nosuid Option to Removable Media Partitionsmedium
pass
Add nodev Option to /tmpmedium
notapplicable
Add noexec Option to /tmpmedium
notapplicable
Add nosuid Option to /tmpmedium
notapplicable
Add nodev Option to /var/log/auditmedium
notapplicable
Add noexec Option to /var/log/auditmedium
notapplicable
Add nosuid Option to /var/log/auditmedium
notapplicable
Add nodev Option to /var/logmedium
notapplicable
Add noexec Option to /var/logmedium
notapplicable
Add nosuid Option to /var/logmedium
notapplicable
Add nodev Option to /var/tmpmedium
notapplicable
Add noexec Option to /var/tmpmedium
notapplicable
Add nosuid Option to /var/tmpmedium
notapplicable
Restrict Programs from Dangerous Execution Patterns 15x fail
Disable Core Dumps 4x fail
Disable acquiring, saving, and processing core dumpsmedium
fail
Disable core dump backtracesmedium
fail
Disable storing core dumpmedium
fail
Disable Core Dumps for All Usersmedium
fail
Enable ExecShield 1x fail
Restrict Exposed Kernel Pointer Addresses Accessmedium
pass
Enable Randomized Layout of Virtual Address Spacemedium
fail
Enable Execute Disable (XD) or No Execute (NX) Support on x86 Systems
Enable NX or XD Support in the BIOSmedium
pass
Memory Poisoning 1x fail
Enable page allocator poisoningmedium
fail
Disable the uvcvideo modulemedium
fail
Disable storing core dumpsmedium
fail
Restrict Access to Kernel Message Bufferlow
fail
Disable Kernel Image Loadingmedium
fail
Disallow kernel profiling by unprivileged userslow
fail
Disable Access to Network bpf() Syscall From Unprivileged Processesmedium
fail
Restrict usage of ptrace to descendant processesmedium
fail
Harden the operation of the BPF just-in-time compilermedium
fail
Disable the use of user namespacesmedium
fail
SELinux 1x fail 1x notchecked
Install policycoreutils Packagelow
pass
Elevate The SELinux Context When An Administrator Calls The Sudo Commandmedium
fail
Configure SELinux Policymedium
pass
Ensure SELinux State is Enforcinghigh
pass
Services 35x fail 2x notchecked
Base Services 1x fail
Uninstall Automatic Bug Reporting Tool (abrt)medium
pass
Disable KDump Kernel Crash Analyzer (kdump)medium
fail
Application Whitelisting Daemon 3x fail
Install fapolicyd Packagemedium
fail
Enable the File Access Policy Servicemedium
fail
Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs.medium
fail
FTP Server
Disable vsftpd if Possible
Uninstall vsftpd Packagehigh
pass
Kerberos
Remove the Kerberos Server Packagemedium
notapplicable
Disable Kerberos by removing host keytabmedium
notapplicable
Mail Server Software 2x fail
Configure SMTP For Mail Clients
Configure System to Forward All Mail From Postmaster to The Root Accountmedium
pass
Configure Operating System to Protect Mail Server
Configure Postfix if Necessary
Control Mail Relaying
Prevent Unrestricted Mail Relayingmedium
notapplicable
The mailx Package Is Installedmedium
fail
The Postfix package is installedmedium
fail
Uninstall Sendmail Packagemedium
pass
NFS and RPC
Configure NFS Clients
Mount Remote Filesystems with Restrictive Options
Mount Remote Filesystems with nodevmedium
notapplicable
Mount Remote Filesystems with noexecmedium
notapplicable
Mount Remote Filesystems with nosuidmedium
notapplicable
Network Time Protocol 4x fail
A remote time server for Chrony is configuredmedium
pass
Disable chrony daemon from acting as serverlow
fail
Disable network management of chrony daemonlow
fail
Configure Time Service Maxpoll Intervalmedium
fail
Ensure Chrony is only configured with the server directivemedium
fail
Obsolete Services
Rlogin, Rsh, and Rexec
Remove Host-Based Authentication Fileshigh
pass
Remove User Host-Based Authentication Fileshigh
pass
Telnet
Uninstall telnet-server Packagehigh
pass
TFTP Server
Uninstall tftp-server Packagehigh
pass
Ensure tftp systemd Service Uses Secure Modemedium
notapplicable
Hardware RNG Entropy Gatherer Daemon 1x fail
Enable the Hardware RNG Entropy Gatherer Servicelow
fail
SSH Server 16x fail 1x notchecked
Configure OpenSSH Client if Necessary 1x notchecked
Verify the SSH Private Key Files Have a Passcodemedium
notchecked
Configure OpenSSH Server if Necessary 16x fail
Set SSH Client Alive Count Maxmedium
fail
Set SSH Client Alive Intervalmedium
fail
Disable SSH Access via Empty Passwordshigh
fail
Disable GSSAPI Authenticationmedium
fail
Disable Kerberos Authenticationmedium
fail
Disable SSH Root Loginmedium
fail
Disable SSH Support for User Known Hostsmedium
fail
Disable X11 Forwardingmedium
fail
Do Not Allow SSH Environment Optionsmedium
fail
Enable Use of Strict Mode Checkingmedium
fail
Enable SSH Warning Bannermedium
fail
Enable SSH Print Last Logmedium
fail
Force frequent session key renegotiationmedium
fail
Use Only FIPS 140-2 Validated Key Exchange Algorithmsmedium
fail
SSH server uses strong entropy to seedlow
fail
Prevent remote hosts from connecting to the proxy displaymedium
fail
Install the OpenSSH Server Packagemedium
pass
Enable the OpenSSH Servicemedium
pass
Verify Permissions on SSH Server Private *_key Key Filesmedium
pass
Verify Permissions on SSH Server Public *.pub Key Filesmedium
pass
System Security Services Daemon 3x fail 1x notchecked
Certificate status checking in SSSDmedium
fail
Enable Certmap in SSSDmedium
fail
Enable Smartcards in SSSDmedium
fail
SSSD Has a Correct Trust Anchormedium
notchecked
Configure SSSD to Expire Offline Credentialsmedium
pass
USBGuard daemon 3x fail
Install usbguard Packagemedium
fail
Enable the USBGuard Servicemedium
fail
Log USBGuard daemon audit events using Linux Auditlow
notapplicable
Generate USBGuard Policymedium
fail
X Window System 2x fail
Disable X Windows 2x fail
Disable graphical user interfacemedium
fail
Disable Graphical Environment Startup By Setting Default Targetmedium
fail
System Accounting with auditd 75x fail 1x notchecked
Configure auditd Rules for Comprehensive Auditing 68x fail
Record Events that Modify the System's Discretionary Access Controls 13x fail
Record Events that Modify the System's Discretionary Access Controls - chmodmedium
fail
Record Events that Modify the System's Discretionary Access Controls - chownmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fchmodmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fchmodatmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fchownmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fchownatmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fremovexattrmedium
fail
Record Events that Modify the System's Discretionary Access Controls - fsetxattrmedium
fail
Record Events that Modify the System's Discretionary Access Controls - lchownmedium
fail
Record Events that Modify the System's Discretionary Access Controls - lremovexattrmedium
fail
Record Events that Modify the System's Discretionary Access Controls - lsetxattrmedium
fail
Record Events that Modify the System's Discretionary Access Controls - removexattrmedium
fail
Record Events that Modify the System's Discretionary Access Controls - setxattrmedium
fail
Record Execution Attempts to Run ACL Privileged Commands 2x fail
Record Any Attempts to Run chaclmedium
fail
Record Any Attempts to Run setfaclmedium
fail
Record Execution Attempts to Run SELinux Privileged Commands 4x fail
Record Any Attempts to Run chconmedium
fail
Record Any Attempts to Run semanagemedium
fail
Record Any Attempts to Run setfilesmedium
fail
Record Any Attempts to Run setseboolmedium
fail
Record File Deletion Events by User 5x fail
Ensure auditd Collects File Deletion Events by User - renamemedium
fail
Ensure auditd Collects File Deletion Events by User - renameatmedium
fail
Ensure auditd Collects File Deletion Events by User - rmdirmedium
fail
Ensure auditd Collects File Deletion Events by User - unlinkmedium
fail
Ensure auditd Collects File Deletion Events by User - unlinkatmedium
fail
Record Unauthorized Access Attempts Events to Files (unsuccessful) 6x fail
Record Unsuccessful Access Attempts to Files - creatmedium
fail
Record Unsuccessful Access Attempts to Files - ftruncatemedium
fail
Record Unsuccessful Access Attempts to Files - openmedium
fail
Record Unsuccessful Access Attempts to Files - open_by_handle_atmedium
fail
Record Unsuccessful Access Attempts to Files - openatmedium
fail
Record Unsuccessful Access Attempts to Files - truncatemedium
fail
Record Information on Kernel Modules Loading and Unloading 3x fail
Ensure auditd Collects Information on Kernel Module Unloading - delete_modulemedium
fail
Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_modulemedium
fail
Ensure auditd Collects Information on Kernel Module Loading - init_modulemedium
fail
Record Attempts to Alter Logon and Logout Events - faillockmedium
fail
Record Attempts to Alter Logon and Logout Events - lastlogmedium
fail
Record Information on the Use of Privileged Commands 20x fail
Ensure auditd Collects Information on the Use of Privileged Commands - chagemedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - chshmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - crontabmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - gpasswdmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - kmodmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - mountmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - newgrpmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_checkmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - passwdmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - postdropmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - postqueuemedium
fail
Record Any Attempts to Run ssh-agentmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysignmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - sumedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - sudomedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - umountmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwdmedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - unix_updatemedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - userhelpermedium
fail
Ensure auditd Collects Information on the Use of Privileged Commands - usermodmedium
fail
Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/medium
fail
Make the auditd Configuration Immutablemedium
fail
Configure immutable Audit login UIDsmedium
fail
Ensure auditd Collects Information on Exporting to Media (successful)medium
fail
Ensure auditd Collects System Administrator Actions - /etc/sudoersmedium
fail
Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/medium
fail
Record Events When Privileged Executables Are Runmedium
fail
Record Events that Modify User/Group Information - /etc/groupmedium
fail
Record Events that Modify User/Group Information - /etc/gshadowmedium
fail
Record Events that Modify User/Group Information - /etc/security/opasswdmedium
fail
Record Events that Modify User/Group Information - /etc/passwdmedium
fail
Record Events that Modify User/Group Information - /etc/shadowmedium
fail
Ensure auditd Collects Changes to Cron Jobs - /var/spool/cronmedium
fail
System Audit Directories Must Be Group Owned By Rootmedium
pass
System Audit Directories Must Be Owned By Rootmedium
pass
System Audit Logs Must Have Mode 0750 or Less Permissivemedium
pass
System Audit Logs Must Be Group Owned By Rootmedium
pass
System Audit Logs Must Be Owned By Rootmedium
pass
System Audit Logs Must Have Mode 0640 or Less Permissivemedium
pass
Configure auditd Data Retention 5x fail 1x notchecked
Configure a Sufficiently Large Partition for Audit Logsmedium
notchecked
Configure auditd Disk Error Action on Disk Errormedium
fail
Configure auditd Disk Full Action when Disk Space Is Fullmedium
fail
Configure auditd mail_acct Action on Low Disk Spacemedium
pass
Configure auditd space_left Action on Low Disk Spacemedium
fail
Configure auditd space_left on Low Disk Spacemedium
fail
Include Local Events in Audit Logsmedium
pass
Resolve information before writing to audit logslow
pass
Set type of computer node name logging in audit logsmedium
fail
Appropriate Action Must be Setup When the Internal Audit Event Queue is Fullmedium
pass
System Accounting with auditd
Verify Permissions on /etc/audit/auditd.confmedium
pass
Verify Permissions on /etc/audit/rules.d/*.rulesmedium
pass
Ensure the audit Subsystem is Installedmedium
pass
Enable auditd Servicemedium
pass
Enable Auditing for Processes Which Start Prior to the Audit Daemonlow
fail
Extend Audit Backlog Limit for the Audit Daemonlow
fail

Result Details

Install AIDExccdf_org.ssgproject.content_rule_package_aide_installed mediumCCE-80844-4

Install AIDE

Rule IDxccdf_org.ssgproject.content_rule_package_aide_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_aide_installed:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-80844-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 2, 3, 5, 7, 8, 9
cjis5.10.1.3
cobit5APO01.06, BAI01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS04.07, DSS05.02, DSS05.03, DSS05.05, DSS05.07, DSS06.02, DSS06.06
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 6.2, SR 7.6
ism1034, 1288, 1341, 1417
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.4.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1, A.8.2.3
nistCM-6(a)
nist-csfDE.CM-1, DE.CM-7, PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, PR.IP-3
pcidssReq-11.5
os-srgSRG-OS-000445-GPOS-00199
anssiR76, R79
cis5.3.1
pcidss411.5.2
stigidRHEL-08-010359
stigrefSV-251710r958944_rule
Description
The aide package can be installed with the following command:
$ sudo yum install aide
Rationale
The AIDE package must be installed if it is to be available for integrity checking.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80844-4
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_aide_installed

- name: Ensure aide is installed
  ansible.builtin.package:
    name: aide
    state: present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80844-4
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_aide_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_aide

class install_aide {
  package { 'aide':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=aide


[[packages]]
name = "aide"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install aide

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install aide
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide
Build and Test AIDE Databasexccdf_org.ssgproject.content_rule_aide_build_database mediumCCE-80675-2

Build and Test AIDE Database

Rule IDxccdf_org.ssgproject.content_rule_aide_build_database
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-aide_build_database:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-80675-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 2, 3, 5, 7, 8, 9
cjis5.10.1.3
cobit5APO01.06, BAI01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS04.07, DSS05.02, DSS05.03, DSS05.05, DSS05.07, DSS06.02, DSS06.06
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 6.2, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.4.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1, A.8.2.3
nistCM-6(a)
nist-csfDE.CM-1, DE.CM-7, PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, PR.IP-3
pcidssReq-11.5
os-srgSRG-OS-000445-GPOS-00199
anssiR76, R79
cis5.3.1
pcidss411.5.2
stigidRHEL-08-010359
stigrefSV-251710r958944_rule
Description
Run the following command to generate a new database:
$ sudo /usr/sbin/aide --init
By default, the database will be written to the file /var/lib/aide/aide.db.new.gz. Storing the database, the configuration file /etc/aide.conf, and the binary /usr/sbin/aide (or hashes of these files), in a secure location (such as on read-only media) provides additional assurance about their integrity. The newly-generated database can be installed as follows:
$ sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
To initiate a manual check, run the following command:
$ sudo /usr/sbin/aide --check
If this check produces any unexpected output, investigate.
Rationale
For AIDE to be effective, an initial database of "known-good" information about files must be captured and it should be able to be verified against the installed files.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi

/usr/sbin/aide --init
/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80675-2
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - aide_build_database
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Build and Test AIDE Database - Ensure AIDE Is Installed
  ansible.builtin.package:
    name: '{{ item }}'
    state: present
  with_items:
  - aide
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80675-2
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - aide_build_database
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Build and Test AIDE Database - Build and Test AIDE Database
  ansible.builtin.command: /usr/sbin/aide --init
  changed_when: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80675-2
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - aide_build_database
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Build and Test AIDE Database - Check Whether the Stock AIDE Database Exists
  ansible.builtin.stat:
    path: /var/lib/aide/aide.db.new.gz
  register: aide_database_stat
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80675-2
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - aide_build_database
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Build and Test AIDE Database - Stage AIDE Database
  ansible.builtin.copy:
    src: /var/lib/aide/aide.db.new.gz
    dest: /var/lib/aide/aide.db.gz
    backup: true
    remote_src: true
  when:
  - '"kernel" in ansible_facts.packages'
  - (aide_database_stat.stat.exists is defined and aide_database_stat.stat.exists)
  tags:
  - CCE-80675-2
  - CJIS-5.10.1.3
  - DISA-STIG-RHEL-08-010359
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-11.5
  - PCI-DSSv4-11.5.2
  - aide_build_database
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide

Testing existence of operational aide database file  oval:ssg-test_aide_operational_database_absolute_path:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_operational_database_absolute_path:obj:1 of type file_object
Filepath
Referenced variable has no values (oval:ssg-variable_aide_operational_database_absolute_path:var:1)
Configure AIDE to Verify the Audit Toolsxccdf_org.ssgproject.content_rule_aide_check_audit_tools mediumCCE-85964-5

Configure AIDE to Verify the Audit Tools

Rule IDxccdf_org.ssgproject.content_rule_aide_check_audit_tools
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-aide_check_audit_tools:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-85964-5

References:
nistAU-9(3), AU-9(3).1
os-srgSRG-OS-000278-GPOS-00108
cis5.3.3
stigidRHEL-08-030650
stigrefSV-230475r1017266_rule
Description
The operating system file integrity tool must be configured to protect the integrity of the audit tools.
Rationale
Protecting the integrity of the tools used for auditing purposes is a critical step toward ensuring the integrity of audit information. Audit information includes all information (e.g., audit records, audit settings, and audit reports) needed to successfully audit information system activity. Audit tools include but are not limited to vendor-provided and open-source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. It is not uncommon for attackers to replace the audit tools or inject code into the existing tools to provide the capability to hide or erase system activity from the audit logs. To address this risk, audit tools must be cryptographically signed to provide the capability to identify when the audit tools have been modified, manipulated, or replaced. An example is a checksum hash of the file or files.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi










if grep -i '^.*/usr/sbin/auditctl.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/auditctl.*#/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/auditd.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/auditd.*#/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/ausearch.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/ausearch.*#/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/aureport.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/aureport.*#/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/autrace.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/autrace.*#/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/augenrules.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/augenrules.*#/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

if grep -i '^.*/usr/sbin/rsyslogd.*$' /etc/aide.conf; then
sed -i "s#.*/usr/sbin/rsyslogd.*#/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512#" /etc/aide.conf
else
echo "/usr/sbin/rsyslogd p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure AIDE to Verify the Audit Tools - Gather List of Packages
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  ansible.builtin.package_facts:
    manager: auto
  when: '"kernel" in ansible_facts.packages'

- name: Ensure aide is installed
  ansible.builtin.package:
    name: '{{ item }}'
    state: present
  with_items:
  - aide
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure AIDE to Verify the Audit Tools - Gather the package facts
  ansible.builtin.package_facts:
    manager: auto
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set audit_tools fact
  ansible.builtin.set_fact:
    audit_tools:
    - /usr/sbin/auditctl
    - /usr/sbin/auditd
    - /usr/sbin/augenrules
    - /usr/sbin/aureport
    - /usr/sbin/ausearch
    - /usr/sbin/autrace
    - /usr/sbin/rsyslogd
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure existing AIDE configuration for audit tools are correct
  ansible.builtin.lineinfile:
    path: /etc/aide.conf
    regexp: ^{{ item }}\s
    line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512'
    create: true
  with_items: '{{ audit_tools }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"aide" in ansible_facts.packages'
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure AIDE to properly protect audit tools
  ansible.builtin.lineinfile:
    path: /etc/aide.conf
    line: '{{ item }} p+i+n+u+g+s+b+acl+xattrs+sha512'
    create: true
  with_items: '{{ audit_tools }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"aide" in ansible_facts.packages'
  tags:
  - CCE-85964-5
  - DISA-STIG-RHEL-08-030650
  - NIST-800-53-AU-9(3)
  - NIST-800-53-AU-9(3).1
  - aide_check_audit_tools
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide

auditctl is checked in /etc/aide.conf  oval:ssg-test_aide_verify_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^\/usr\/sbin\/auditctl\s+([^\n]+)$1

auditd is checked in /etc/aide.conf  oval:ssg-test_aide_verify_auditd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_auditd:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/auditd\s+([^\n]+)$1

ausearch is checked in /etc/aide.conf  oval:ssg-test_aide_verify_ausearch:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_ausearch:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/ausearch\s+([^\n]+)$1

aureport is checked in /etc/aide.conf  oval:ssg-test_aide_verify_aureport:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_aureport:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/aureport\s+([^\n]+)$1

autrace is checked in /etc/aide.conf  oval:ssg-test_aide_verify_autrace:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_autrace:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/autrace\s+([^\n]+)$1

rsyslogd is checked in /etc/aide.conf  oval:ssg-test_aide_verify_rsyslogd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_rsyslogd:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/rsyslogd\s+([^\n]+)$1

augenrules is checked in /etc/aide.conf  oval:ssg-test_aide_verify_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^/usr/sbin/augenrules\s+([^\n]+)$1
Configure Notification of Post-AIDE Scan Detailsxccdf_org.ssgproject.content_rule_aide_scan_notification mediumCCE-82891-3

Configure Notification of Post-AIDE Scan Details

Rule IDxccdf_org.ssgproject.content_rule_aide_scan_notification
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-aide_scan_notification:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-82891-3

References:
cis-csc1, 11, 12, 13, 15, 16, 2, 3, 5, 7, 8, 9
cobit5BAI01.06, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS05.02, DSS05.05, DSS05.07
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 6.2, SR 7.6
iso27001-2013A.12.1.2, A.12.4.1, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1
nistCM-6(a), CM-3(5)
nist-csfDE.CM-1, DE.CM-7, PR.IP-1, PR.IP-3
os-srgSRG-OS-000363-GPOS-00150, SRG-OS-000446-GPOS-00200, SRG-OS-000447-GPOS-00201
anssiR76
stigidRHEL-08-010360
stigrefSV-230263r1017083_rule
Description
AIDE should notify appropriate personnel of the details of a scan after the scan has been run. If AIDE has already been configured for periodic execution in /etc/crontab, append the following line to the existing AIDE line:
 | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost
Otherwise, add the following line to /etc/crontab:
05 4 * * * root /usr/sbin/aide --check | /bin/mail -s "$(hostname) - AIDE Integrity Check" root@localhost
AIDE can be executed periodically through other means; this is merely one example.
Rationale
Unauthorized changes to the baseline configuration could make the system vulnerable to various attacks or allow unauthorized access to the operating system. Changes to operating system configurations can have unintended side effects, some of which may be relevant to security.

Detecting such changes and providing an automated response can help avoid unintended, negative consequences that could ultimately affect the security state of the operating system. The operating system's Information Management Officer (IMO)/Information System Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or monitoring system trap when there is an unauthorized modification of a configuration item.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi
var_aide_scan_notification_email='root@localhost'



CRONTAB=/etc/crontab
CRONDIRS='/etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.monthly'

# NOTE: on some platforms, /etc/crontab may not exist
if [ -f /etc/crontab ]; then
	CRONTAB_EXIST=/etc/crontab
fi

if [ -f /var/spool/cron/root ]; then
	VARSPOOL=/var/spool/cron/root
fi

if ! grep -qR '^.*/usr/sbin/aide\s*\-\-check.*|.*\/bin\/mail\s*-s\s*".*"\s*.*@.*$' $CRONTAB_EXIST $VARSPOOL $CRONDIRS; then
	echo "0 5 * * * root /usr/sbin/aide  --check | /bin/mail -s \"\$(hostname) - AIDE Integrity Check\" $var_aide_scan_notification_email" >> $CRONTAB
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82891-3
  - DISA-STIG-RHEL-08-010360
  - NIST-800-53-CM-3(5)
  - NIST-800-53-CM-6(a)
  - aide_scan_notification
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_aide_scan_notification_email # promote to variable
  set_fact:
    var_aide_scan_notification_email: !!str root@localhost
  tags:
    - always

- name: Ensure AIDE is installed
  ansible.builtin.package:
    name: '{{ item }}'
    state: present
  with_items:
  - aide
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82891-3
  - DISA-STIG-RHEL-08-010360
  - NIST-800-53-CM-3(5)
  - NIST-800-53-CM-6(a)
  - aide_scan_notification
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Notification of Post-AIDE Scan Details
  ansible.builtin.cron:
    name: run AIDE check
    minute: 5
    hour: 4
    weekday: 0
    user: root
    job: /usr/sbin/aide  --check | /bin/mail -s "$(hostname) - AIDE Integrity Check"
      {{ var_aide_scan_notification_email }}
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82891-3
  - DISA-STIG-RHEL-08-010360
  - NIST-800-53-CM-3(5)
  - NIST-800-53-CM-6(a)
  - aide_scan_notification
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide

notify personnel when aide completes  oval:ssg-test_aide_scan_notification:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_test_aide_scan_notification:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/crontab^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$1

notify personnel when aide completes  oval:ssg-test_aide_var_cron_notification:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_var_cron_notification:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/var/spool/cron/root^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$1

notify personnel when aide completes in cron.(daily|weekly|monthly)  oval:ssg-test_aide_crontabs_notification:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_crontabs_notification:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
^/etc/cron.(d|daily|weekly|monthly)$^.*$^.*/usr/sbin/aide[\s]*\-\-check.*\|.*/bin/mail[\s]*-s[\s]*".*"[\s]*.+@.+$1
Configure AIDE to Verify Access Control Lists (ACLs)xccdf_org.ssgproject.content_rule_aide_verify_acls lowCCE-84220-3

Configure AIDE to Verify Access Control Lists (ACLs)

Rule IDxccdf_org.ssgproject.content_rule_aide_verify_acls
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-aide_verify_acls:def:1
Time2025-11-18T17:04:59-05:00
Severitylow
Identifiers:

CCE-84220-3

References:
cis-csc2, 3
cobit5APO01.06, BAI03.05, BAI06.01, DSS06.02
isa-62443-20094.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8
iso27001-2013A.11.2.4, A.12.2.1, A.12.5.1, A.14.1.2, A.14.1.3, A.14.2.4
nistSI-7, SI-7(1), CM-6(a)
nist-csfPR.DS-6, PR.DS-8
os-srgSRG-OS-000480-GPOS-00227
anssiR76
stigidRHEL-08-040310
stigrefSV-230552r1101902_rule
Description
By default, the acl option is added to the FIPSR ruleset in AIDE. If using a custom ruleset or the acl option is missing, add acl to the appropriate ruleset. For example, add acl to the following line in /etc/aide.conf:
FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
AIDE rules can be configured in multiple ways; this is merely one example that is already configured by default. The remediation provided with this rule adds acl to all rule sets available in /etc/aide.conf
Rationale
ACLs can provide permissions beyond those permitted through the file mode and must be verified by the file integrity tools.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi

aide_conf="/etc/aide.conf"


groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u)


for group in $groups
do
	config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ')

	if ! [[ $config = *acl* ]]
	then
		if [[ -z $config ]]
		then
			config="acl"
		else
			config=$config"+acl"
		fi
	fi
	sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84220-3
  - DISA-STIG-RHEL-08-040310
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_acls
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Gather list of packages
  ansible.builtin.package_facts:
    manager: auto
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-84220-3
  - DISA-STIG-RHEL-08-040310
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_acls
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Get rules groups
  ansible.builtin.shell: |
    set -o pipefail
    LC_ALL=C grep "^[A-Z][A-Za-z_]*" /etc/aide.conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u || true
  when:
  - '"kernel" in ansible_facts.packages'
  - '''aide'' in ansible_facts.packages'
  register: find_rules_groups_results
  tags:
  - CCE-84220-3
  - DISA-STIG-RHEL-08-040310
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_acls
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the acl rule is present when aide is installed.
  ansible.builtin.replace:
    path: /etc/aide.conf
    regexp: (^\s*{{ item }}\s*=\s*)(?!.*acl)([^\s]*)
    replace: \g<1>\g<2>+acl
  when:
  - '"kernel" in ansible_facts.packages'
  - find_rules_groups_results is not skipped and "'aide' in ansible_facts.packages"
  with_items: '{{ find_rules_groups_results.stdout_lines | map(''trim'') | list }}'
  tags:
  - CCE-84220-3
  - DISA-STIG-RHEL-08-040310
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_acls
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide

acl is set in /etc/aide.conf  oval:ssg-test_aide_verify_acls:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_acls:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$1
Configure AIDE to Verify Extended Attributesxccdf_org.ssgproject.content_rule_aide_verify_ext_attributes lowCCE-83733-6

Configure AIDE to Verify Extended Attributes

Rule IDxccdf_org.ssgproject.content_rule_aide_verify_ext_attributes
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-aide_verify_ext_attributes:def:1
Time2025-11-18T17:04:59-05:00
Severitylow
Identifiers:

CCE-83733-6

References:
cis-csc2, 3
cobit5APO01.06, BAI03.05, BAI06.01, DSS06.02
isa-62443-20094.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8
iso27001-2013A.11.2.4, A.12.2.1, A.12.5.1, A.14.1.2, A.14.1.3, A.14.2.4
nistSI-7, SI-7(1), CM-6(a)
nist-csfPR.DS-6, PR.DS-8
os-srgSRG-OS-000480-GPOS-00227
anssiR76
stigidRHEL-08-040300
stigrefSV-230551r1017313_rule
Description
By default, the xattrs option is added to the FIPSR ruleset in AIDE. If using a custom ruleset or the xattrs option is missing, add xattrs to the appropriate ruleset. For example, add xattrs to the following line in /etc/aide.conf:
FIPSR = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha256
AIDE rules can be configured in multiple ways; this is merely one example that is already configured by default. The remediation provided with this rule adds xattrs to all rule sets available in /etc/aide.conf
Rationale
Extended attributes in file systems are used to contain arbitrary data and file metadata with security implications.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "aide" ; then
    yum install -y "aide"
fi

aide_conf="/etc/aide.conf"


groups=$(LC_ALL=C grep "^[A-Z][A-Za-z_]*" $aide_conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u)


for group in $groups
do
	config=$(grep "^$group\s*=" $aide_conf | cut -f2 -d '=' | tr -d ' ')

	if ! [[ $config = *xattrs* ]]
	then
		if [[ -z $config ]]
		then
			config="xattrs"
		else
			config=$config"+xattrs"
		fi
	fi
	sed -i "s/^$group\s*=.*/$group = $config/g" $aide_conf
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83733-6
  - DISA-STIG-RHEL-08-040300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_ext_attributes
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Gather list of packages
  ansible.builtin.package_facts:
    manager: auto
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-83733-6
  - DISA-STIG-RHEL-08-040300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_ext_attributes
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Get rules groups
  ansible.builtin.shell: |
    set -o pipefail
    LC_ALL=C grep "^[A-Z][A-Za-z_]*" /etc/aide.conf | grep -v "^ALLXTRAHASHES" | cut -f1 -d '=' | tr -d ' ' | sort -u || true
  when:
  - '"kernel" in ansible_facts.packages'
  - '''aide'' in ansible_facts.packages'
  register: find_rules_groups_results
  tags:
  - CCE-83733-6
  - DISA-STIG-RHEL-08-040300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_ext_attributes
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the xattrs rule is present when aide is installed.
  ansible.builtin.replace:
    path: /etc/aide.conf
    regexp: (^\s*{{ item }}\s*=\s*)(?!.*xattrs)([^\s]*)
    replace: \g<1>\g<2>+xattrs
  when:
  - '"kernel" in ansible_facts.packages'
  - find_rules_groups_results is not skipped and "'aide' in ansible_facts.packages"
  with_items: '{{ find_rules_groups_results.stdout_lines | map(''trim'') | list }}'
  tags:
  - CCE-83733-6
  - DISA-STIG-RHEL-08-040300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-7
  - NIST-800-53-SI-7(1)
  - aide_verify_ext_attributes
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

package aide is installed  oval:ssg-test_package_aide_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_aide_installed:obj:1 of type rpminfo_object
Name
aide

xattrs is set in /etc/aide.conf  oval:ssg-test_aide_verify_ext_attributes:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_aide_verify_ext_attributes:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/aide.conf^(?!ALLXTRAHASHES)[A-Z][a-zA-Z_]*[\s]*=[\s]*([a-zA-Z0-9\+]*)$1
Audit Tools Must Be Group-owned by Rootxccdf_org.ssgproject.content_rule_file_audit_tools_group_ownership mediumCCE-86239-1

Audit Tools Must Be Group-owned by Root

Rule IDxccdf_org.ssgproject.content_rule_file_audit_tools_group_ownership
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_audit_tools_group_ownership:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-86239-1

References:
nistAU-9
os-srgSRG-OS-000256-GPOS-00097, SRG-OS-000257-GPOS-00098, SRG-OS-000258-GPOS-00099
stigidRHEL-08-030640
stigrefSV-230474r1017265_rule
Description
Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. Audit tools must have the correct group owner.
Rationale
Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information.
OVAL test results details

Testing group ownership of /sbin/auditctl  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_0:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditctloval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/aureport  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_1:obj:1 of type file_object
FilepathFilterFilter
/sbin/aureportoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/ausearch  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_2:obj:1 of type file_object
FilepathFilterFilter
/sbin/ausearchoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/autrace  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_3:obj:1 of type file_object
FilepathFilterFilter
/sbin/autraceoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/auditd  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_4:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_4:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditdoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/rsyslogd  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_5:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_5:obj:1 of type file_object
FilepathFilterFilter
/sbin/rsyslogdoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1

Testing group ownership of /sbin/augenrules  oval:ssg-test_file_groupownerfile_audit_tools_group_ownership_6:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerfile_audit_tools_group_ownership_6:obj:1 of type file_object
FilepathFilterFilter
/sbin/augenrulesoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerfile_audit_tools_group_ownership_0_0:ste:1
Audit Tools Must Be Owned by Rootxccdf_org.ssgproject.content_rule_file_audit_tools_ownership mediumCCE-86259-9

Audit Tools Must Be Owned by Root

Rule IDxccdf_org.ssgproject.content_rule_file_audit_tools_ownership
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_audit_tools_ownership:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-86259-9

References:
nistAU-9
os-srgSRG-OS-000256-GPOS-00097, SRG-OS-000257-GPOS-00098, SRG-OS-000258-GPOS-00099
stigidRHEL-08-030630
stigrefSV-230473r1017264_rule
Description
Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. Audit tools must have the correct owner.
Rationale
Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information.
OVAL test results details

Testing user ownership of /sbin/auditctl  oval:ssg-test_file_ownerfile_audit_tools_ownership_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_0:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditctloval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/aureport  oval:ssg-test_file_ownerfile_audit_tools_ownership_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_1:obj:1 of type file_object
FilepathFilterFilter
/sbin/aureportoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/ausearch  oval:ssg-test_file_ownerfile_audit_tools_ownership_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_2:obj:1 of type file_object
FilepathFilterFilter
/sbin/ausearchoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/autrace  oval:ssg-test_file_ownerfile_audit_tools_ownership_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_3:obj:1 of type file_object
FilepathFilterFilter
/sbin/autraceoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/auditd  oval:ssg-test_file_ownerfile_audit_tools_ownership_4:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_4:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditdoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/rsyslogd  oval:ssg-test_file_ownerfile_audit_tools_ownership_5:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_5:obj:1 of type file_object
FilepathFilterFilter
/sbin/rsyslogdoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1

Testing user ownership of /sbin/augenrules  oval:ssg-test_file_ownerfile_audit_tools_ownership_6:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerfile_audit_tools_ownership_6:obj:1 of type file_object
FilepathFilterFilter
/sbin/augenrulesoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerfile_audit_tools_ownership_0_0:ste:1
Audit Tools Must Have a Mode of 0755 or Less Permissivexccdf_org.ssgproject.content_rule_file_audit_tools_permissions mediumCCE-86227-6

Audit Tools Must Have a Mode of 0755 or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_file_audit_tools_permissions
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_audit_tools_permissions:def:1
Time2025-11-18T17:04:59-05:00
Severitymedium
Identifiers:

CCE-86227-6

References:
nistAU-9
os-srgSRG-OS-000256-GPOS-00097, SRG-OS-000257-GPOS-00098, SRG-OS-000258-GPOS-00099
stigidRHEL-08-030620
stigrefSV-230472r1017263_rule
Description
Red Hat Enterprise Linux 8 systems providing tools to interface with audit information will leverage user permissions and roles identifying the user accessing the tools, and the corresponding rights the user enjoys, to make access decisions regarding the access to audit tools. Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. Audit tools must have a mode of 0755 or less permissive.
Rationale
Protecting audit information also includes identifying and protecting the tools used to view and manipulate log data. Therefore, protecting audit tools is necessary to prevent unauthorized operations on audit information.
OVAL test results details

Testing mode of /sbin/auditctl  oval:ssg-test_file_permissionsfile_audit_tools_permissions_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_0:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditctloval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_0_mode_0755or_stricter_:ste:1

Testing mode of /sbin/aureport  oval:ssg-test_file_permissionsfile_audit_tools_permissions_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_1:obj:1 of type file_object
FilepathFilterFilter
/sbin/aureportoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_1_mode_0755or_stricter_:ste:1

Testing mode of /sbin/ausearch  oval:ssg-test_file_permissionsfile_audit_tools_permissions_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_2:obj:1 of type file_object
FilepathFilterFilter
/sbin/ausearchoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_2_mode_0755or_stricter_:ste:1

Testing mode of /sbin/autrace  oval:ssg-test_file_permissionsfile_audit_tools_permissions_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_3:obj:1 of type file_object
FilepathFilterFilter
/sbin/autraceoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_3_mode_0755or_stricter_:ste:1

Testing mode of /sbin/auditd  oval:ssg-test_file_permissionsfile_audit_tools_permissions_4:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_4:obj:1 of type file_object
FilepathFilterFilter
/sbin/auditdoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_4_mode_0755or_stricter_:ste:1

Testing mode of /sbin/rsyslogd  oval:ssg-test_file_permissionsfile_audit_tools_permissions_5:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_5:obj:1 of type file_object
FilepathFilterFilter
/sbin/rsyslogdoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_5_mode_0755or_stricter_:ste:1

Testing mode of /sbin/augenrules  oval:ssg-test_file_permissionsfile_audit_tools_permissions_6:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsfile_audit_tools_permissions_6:obj:1 of type file_object
FilepathFilterFilter
/sbin/augenrulesoval:ssg-exclude_symlinks_file_audit_tools_permissions:ste:1oval:ssg-state_file_permissionsfile_audit_tools_permissions_6_mode_0755or_stricter_:ste:1
Enable Dracut FIPS Modulexccdf_org.ssgproject.content_rule_enable_dracut_fips_module highCCE-82155-3

Enable Dracut FIPS Module

Rule IDxccdf_org.ssgproject.content_rule_enable_dracut_fips_module
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-enable_dracut_fips_module:def:1
Time2025-11-18T17:04:59-05:00
Severityhigh
Identifiers:

CCE-82155-3

References:
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-12(2), SC-12(3), IA-7, SC-13, CM-6(a), SC-12
osppFCS_RBG_EXT.1
os-srgSRG-OS-000478-GPOS-00223
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Red Hat Enterprise Linux 8 has an installation-time kernel flag that can enable FIPS mode. The installer must be booted with fips=1 for the system to have FIPS mode enabled. Enabling FIPS mode on a preexisting system is not supported. If this rule fails on an installed system, then this is a permanent finding and cannot be fixed. To enable FIPS, the system requires that the fips module is added in dracut configuration. Check if /etc/dracut.conf.d/40-fips.conf contain add_dracutmodules+=" fips "
Rationale
Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.
Warnings
warning  To configure the operating system to run in FIPS 140 mode, the kernel parameter "fips=1" needs to be added during its installation. Enabling FIPS mode on a preexisting system involves a number of modifications to it and therefore is not supported.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.
OVAL test results details

add_dracutmodules contains fips  oval:ssg-test_enable_dracut_fips_module:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_enable_dracut_fips_module:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/dracut.conf.d/40-fips.conf^\s*add_dracutmodules\+="\s*(\w*)\s*"\s*(?:#.*)?$1
Enable FIPS Modexccdf_org.ssgproject.content_rule_enable_fips_mode highCCE-80942-6

Enable FIPS Mode

Rule IDxccdf_org.ssgproject.content_rule_enable_fips_mode
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-enable_fips_mode:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80942-6

References:
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistCM-3(6), SC-12(2), SC-12(3), IA-7, SC-13, CM-6(a), SC-12
osppFCS_COP.1(1), FCS_COP.1(2), FCS_COP.1(3), FCS_COP.1(4), FCS_CKM.1, FCS_CKM.2, FCS_TLSC_EXT.1, FCS_RBG_EXT.1
os-srgSRG-OS-000478-GPOS-00223, SRG-OS-000396-GPOS-00176
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Red Hat Enterprise Linux 8 has an installation-time kernel flag that can enable FIPS mode. The installer must be booted with fips=1 for the system to have FIPS mode enabled. Enabling FIPS mode on a preexisting system is not supported. If this rule fails on an installed system, then this is a permanent finding and cannot be fixed.
Rationale
Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.
Warnings
warning  To configure Red Hat Enterprise Linux 8 to run in FIPS 140 mode, the kernel parameter "fips=1" needs to be added during its installation. Only enabling FIPS 140 mode during the Red Hat Enterprise Linux 8 installation ensures that the system generates all keys with FIPS-approved algorithms and continuous monitoring tests in place. Enabling FIPS mode on a preexisting system involves a number of modifications to it and therefore is not supported.
warning  This rule DOES NOT CHECK if the components of the operating system are FIPS certified. You can find the list of FIPS certified modules at https://csrc.nist.gov/projects/cryptographic-module-validation-program/validated-modules/search. This rule checks if the system is running in FIPS mode.

# Remediation is applicable only in certain platforms
if ( ! ( [ "${container:-}" == "bwrap-osbuild" ] ) && rpm --quiet -q kernel ); then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; }; then
	cat > /usr/lib/bootc/kargs.d/01-fips.toml << EOF
kargs = ["fips=1"]
EOF
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi


[customizations]
fips = true
OVAL test results details

kernel runtime parameter crypto.fips_enabled set to 1  oval:ssg-test_proc_sys_crypto_fips_enabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_proc_sys_crypto_fips_enabled:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/proc/sys/crypto/fips_enabled^1$1

kernel runtime parameter crypto.fips_enabled set to 1  oval:ssg-test_sysctl_crypto_fips_enabled:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsecrypto.fips_enabled0

add_dracutmodules contains fips  oval:ssg-test_enable_dracut_fips_module:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_enable_dracut_fips_module:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/dracut.conf.d/40-fips.conf^\s*add_dracutmodules\+="\s*(\w*)\s*"\s*(?:#.*)?$1

check for crypto policy correctly configured in /etc/crypto-policies/config  oval:ssg-test_configure_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/configDEFAULT

check for crypto policy correctly configured in /etc/crypto-policies/state/current  oval:ssg-test_configure_crypto_policy_current:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/state/currentDEFAULT

Check if update-crypto-policies has been run  oval:ssg-test_crypto_policies_updated:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-variable_crypto_policies_config_file_timestamp:var:11763494113

Check if /etc/crypto-policies/back-ends/nss.config exists  oval:ssg-test_crypto_policy_nss_config:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/etc/crypto-policies/back-ends/nss.configregular00452rw-r--r-- 

test if var_system_crypto_policy selection is set to FIPS  oval:ssg-test_system_crypto_policy_value:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_system_crypto_policy:var:1FIPS

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

check if kernel option fips=1 is present in options in /boot/loader/entries/.*.conf  oval:ssg-test_fips_1_argument_in_boot_loader_entries_conf:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params
false/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-0-rescue.confoptions $kernelopts

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

FIPS mode is selected in running kernel options  oval:ssg-test_grubenv_fips_mode:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_grubenv_fips_mode:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/grub2/grubenvfips=11
FIPS Must Use a Supported Subpolicyxccdf_org.ssgproject.content_rule_fips_crypto_subpolicy mediumCCE-86536-0

FIPS Must Use a Supported Subpolicy

Rule IDxccdf_org.ssgproject.content_rule_fips_crypto_subpolicy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-fips_crypto_subpolicy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86536-0

References:
os-srgSRG-OS-000033-GPOS-00014
stigidRHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Sub-policies can be used to modify existing crypto policies. Some sub-policies such as NO-ENFORCE-EMS reduce the security of the system and should not be used. Other such as AD-SUPPORT should only be enabled if operationally required. The OSPP, NO-SHA1, NO-CAMELLIA, and ECDHE-ONLY are allowed by this rule.
Rationale
Sub-policies can cause insecure ciphers to be used.
Warnings
warning  This rule does not have a remediation.
OVAL test results details

Correct sub policy enabled  oval:ssg-test_fips_crypto_subpolicy:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_fips_crypto_subpolicy:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/crypto-policies/config^FIPS$|^FIPS:(OSPP|NO-SHA1|NO-CAMELLIA|ECDHE-ONLY|STIG)$1
Set kernel parameter 'crypto.fips_enabled' to 1xccdf_org.ssgproject.content_rule_sysctl_crypto_fips_enabled highCCE-84027-2

Set kernel parameter 'crypto.fips_enabled' to 1

Rule IDxccdf_org.ssgproject.content_rule_sysctl_crypto_fips_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_crypto_fips_enabled:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-84027-2

References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-12(2), SC-12(3), IA-7, SC-13, CM-6(a), SC-12
os-srgSRG-OS-000033-GPOS-00014, SRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174, SRG-OS-000396-GPOS-00176, SRG-OS-000423-GPOS-00187, SRG-OS-000478-GPOS-00223
stigidRHEL-08-010020
stigrefSV-230223r1069327_rule
Description
System running in FIPS mode is indicated by kernel parameter 'crypto.fips_enabled'. This parameter should be set to 1 in FIPS mode. Red Hat Enterprise Linux 8 has an installation-time kernel flag that can enable FIPS mode. The installer must be booted with fips=1 for the system to have FIPS mode enabled. Enabling FIPS mode on a preexisting system is not supported. If this rule fails on an installed system, then this is a permanent finding and cannot be fixed. To enable strict FIPS compliance, the fips=1 kernel option needs to be added to the kernel boot parameters during system installation so key generation is done with FIPS-approved algorithms and continuous monitoring tests in place.
Rationale
Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data. The operating system must implement cryptographic modules adhering to the higher standards approved by the federal government since this provides assurance they have been tested and validated.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.
OVAL test results details

kernel runtime parameter crypto.fips_enabled set to 1  oval:ssg-test_sysctl_crypto_fips_enabled:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsecrypto.fips_enabled0
Configure BIND to use System Crypto Policyxccdf_org.ssgproject.content_rule_configure_bind_crypto_policy highCCE-80934-3

Configure BIND to use System Crypto Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_bind_crypto_policy
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80934-3

References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-13, SC-12(2), SC-12(3)
os-srgSRG-OS-000423-GPOS-00187, SRG-OS-000426-GPOS-00190
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. BIND is supported by crypto policy, but the BIND configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that the /etc/named.conf includes the appropriate configuration: In the options section of /etc/named.conf, make sure that the following line is not commented out or superseded by later includes: include "/etc/crypto-policies/back-ends/bind.config";
Rationale
Overriding the system crypto policy makes the behavior of the BIND service violate expectations, and makes system configuration more fragmented.
Configure System Cryptography Policyxccdf_org.ssgproject.content_rule_configure_crypto_policy highCCE-80935-0

Configure System Cryptography Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_crypto_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80935-0

References:
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.312(e)(1), 164.312(e)(2)(ii)
ism1446
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
osppFCS_COP.1(1), FCS_COP.1(2), FCS_COP.1(3), FCS_COP.1(4), FCS_CKM.1, FCS_CKM.2, FCS_TLSC_EXT.1
os-srgSRG-OS-000396-GPOS-00176, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174
cis1.6.1
pcidss42.2.7, 2.2
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
To configure the system cryptography policy to use ciphers only from the FIPS policy, run the following command:
$ sudo update-crypto-policies --set FIPS
The rule checks if settings for selected crypto policy are configured as expected. Configuration files in the /etc/crypto-policies/back-ends are either symlinks to correct files provided by Crypto-policies package or they are regular files in case crypto policy customizations are applied. Crypto policies may be customized by crypto policy modules, in which case it is delimited from the base policy using a colon.
Rationale
Centralized cryptographic policies simplify applying secure ciphers across an operating system and the applications that run on that operating system. Use of weak or untested encryption algorithms undermines the purposes of utilizing encryption to protect data.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_system_crypto_policy='FIPS'




stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null)
rc=$?

if test "$rc" = 127; then
	echo "$stderr_of_call" >&2
	echo "Make sure that the script is installed on the remediated system." >&2
	echo "See output of the 'dnf provides update-crypto-policies' command" >&2
	echo "to see what package to (re)install" >&2

	false  # end with an error code
elif test "$rc" != 0; then
	echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2
	false  # end with an error code
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_system_crypto_policy # promote to variable
  set_fact:
    var_system_crypto_policy: !!str FIPS
  tags:
    - always

- name: Configure System Cryptography Policy
  ansible.builtin.lineinfile:
    path: /etc/crypto-policies/config
    regexp: ^(?!#)(\S+)$
    line: '{{ var_system_crypto_policy }}'
    create: true
  tags:
  - CCE-80935-0
  - DISA-STIG-RHEL-08-010020
  - DISA-STIG-needed_rules
  - NIST-800-53-AC-17(2)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.7
  - configure_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy

- name: Verify that Crypto Policy is Set (runtime)
  ansible.builtin.command: /usr/bin/update-crypto-policies --set {{ var_system_crypto_policy
    }}
  tags:
  - CCE-80935-0
  - DISA-STIG-RHEL-08-010020
  - DISA-STIG-needed_rules
  - NIST-800-53-AC-17(2)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(6)
  - NIST-800-53-SC-12(2)
  - NIST-800-53-SC-12(3)
  - NIST-800-53-SC-13
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.7
  - configure_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
        - name: configure-crypto-policy.service
          enabled: true
          contents: |
            [Unit]
            Before=kubelet.service
            [Service]
            Type=oneshot
            ExecStart=update-crypto-policies --set {{.var_system_crypto_policy}}
            RemainAfterExit=yes
            [Install]
            WantedBy=multi-user.target
OVAL test results details

check for crypto policy correctly configured in /etc/crypto-policies/config  oval:ssg-test_configure_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/configDEFAULT

check for crypto policy correctly configured in /etc/crypto-policies/state/current  oval:ssg-test_configure_crypto_policy_current:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/state/currentDEFAULT

Check if update-crypto-policies has been run  oval:ssg-test_crypto_policies_updated:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-variable_crypto_policies_config_file_timestamp:var:11763494113

Check if /etc/crypto-policies/back-ends/nss.config exists  oval:ssg-test_crypto_policy_nss_config:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/etc/crypto-policies/back-ends/nss.configregular00452rw-r--r-- 
Configure GnuTLS library to use DoD-approved TLS Encryptionxccdf_org.ssgproject.content_rule_configure_gnutls_tls_crypto_policy mediumCCE-84254-2

Configure GnuTLS library to use DoD-approved TLS Encryption

Rule IDxccdf_org.ssgproject.content_rule_configure_gnutls_tls_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_gnutls_tls_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-84254-2

References:
nistAC-17(2)
os-srgSRG-OS-000250-GPOS-00093, SRG-OS-000423-GPOS-00187
stigidRHEL-08-010295
stigrefSV-230256r1017076_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. GnuTLS is supported by system crypto policy, but the GnuTLS configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that /etc/crypto-policies/back-ends/gnutls.config contains the following line and is not commented out: +VERS-ALL:-VERS-DTLS0.9:-VERS-TLS1.1:-VERS-TLS1.0:-VERS-SSL3.0:-VERS-DTLS1.0 These keywords are order-independent, so the line can be in any order. GnuTLS will then prefer the highest version.
Rationale
Overriding the system crypto policy makes the behavior of the GnuTLS library violate expectations, and makes system configuration more fragmented.
OVAL test results details

tests the presence of '+VERS-ALL:-VERS-DTLS0.9:-VERS-TLS1.1:-VERS-TLS1.0:-VERS-SSL3.0:-VERS-DTLS1.0' setting in the /etc/crypto-policies/back-ends/gnutls.config file  oval:ssg-test_configure_gnutls_tls_crypto_policy:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/crypto-policies/back-ends/gnutls.config+VERS-ALL:-VERS-DTLS0.9:-VERS-TLS1.1:-VERS-TLS1.0:-VERS-SSL3.0:-VERS-DTLS1.0
Configure Kerberos to use System Crypto Policyxccdf_org.ssgproject.content_rule_configure_kerberos_crypto_policy highCCE-80936-8

Configure Kerberos to use System Crypto Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_kerberos_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_kerberos_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80936-8

References:
ism0418, 1055, 1402
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistSC-13, SC-12(2), SC-12(3)
os-srgSRG-OS-000120-GPOS-00061
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. Kerberos is supported by crypto policy, but it's configuration may be set up to ignore it. To check that Crypto Policies settings for Kerberos are configured correctly, examine that there is a symlink at /etc/krb5.conf.d/crypto-policies targeting /etc/cypto-policies/back-ends/krb5.config. If the symlink exists, Kerberos is configured to use the system-wide crypto policy settings.
Rationale
Overriding the system crypto policy makes the behavior of Kerberos violate expectations, and makes system configuration more fragmented.
OVAL test results details

Check if kerberos configuration symlink and crypto policy kerberos backend symlink point to same file  oval:ssg-test_configure_kerberos_crypto_policy_symlink:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_symlink_kerberos_crypto_policy_configuration:var:1/usr/share/crypto-policies/DEFAULT/krb5.txt

Check if kerberos configuration symlink links to the crypto-policy backend file  oval:ssg-test_configure_kerberos_crypto_policy_nosymlink:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-var_symlink_kerberos_crypto_policy_configuration:var:1/usr/share/crypto-policies/DEFAULT/krb5.txt
Configure Libreswan to use System Crypto Policyxccdf_org.ssgproject.content_rule_configure_libreswan_crypto_policy highCCE-80937-6

Configure Libreswan to use System Crypto Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_libreswan_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_libreswan_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80937-6

References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistCM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
pcidssReq-2.2
os-srgSRG-OS-000033-GPOS-00014
stigidneeded_rules, RHEL-08-010020
stigrefSV-230223r1069327_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. Libreswan is supported by system crypto policy, but the Libreswan configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that the /etc/ipsec.conf includes the appropriate configuration file. In /etc/ipsec.conf, make sure that the following line is not commented out or superseded by later includes: include /etc/crypto-policies/back-ends/libreswan.config
Rationale
Overriding the system crypto policy makes the behavior of the Libreswan service violate expectations, and makes system configuration more fragmented.
OVAL test results details

package libreswan is installed  oval:ssg-test_package_libreswan_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_libreswan_installed:obj:1 of type rpminfo_object
Name
libreswan

Check that the libreswan configuration includes the crypto policy config file  oval:ssg-test_configure_libreswan_crypto_policy:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_configure_libreswan_crypto_policy:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ipsec.conf^\s*include\s+/etc/crypto-policies/back-ends/libreswan.config\s*(?:#.*)?$1
Configure OpenSSL library to use System Crypto Policyxccdf_org.ssgproject.content_rule_configure_openssl_crypto_policy mediumCCE-80938-4

Configure OpenSSL library to use System Crypto Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_openssl_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_openssl_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80938-4

References:
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13, SC-12(2), SC-12(3)
osppFCS_CKM.1, FCS_CKM.1.1, FCS_CKM.2, FCS_COP.1/ENCRYPT, FCS_COP.1/HASH, FCS_COP.1/SIGN, FCS_COP.1/KEYHMAC, FCS_TLSC_EXT.1, FCS_TLSC_EXT.1.1
pcidssReq-2.2
os-srgSRG-OS-000250-GPOS-00093
stigidRHEL-08-010293
stigrefSV-230254r1017072_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. OpenSSL is supported by crypto policy, but the OpenSSL configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, you have to examine the OpenSSL config file available under /etc/pki/tls/openssl.cnf. This file has the ini format, and it enables crypto policy support if there is a [ crypto_policy ] section that contains the .include /etc/crypto-policies/back-ends/opensslcnf.config directive.
Rationale
Overriding the system crypto policy makes the behavior of the Java runtime violates expectations, and makes system configuration more fragmented.
OVAL test results details

Check that the configuration mandates usage of system-wide crypto policies.  oval:ssg-test_configure_openssl_crypto_policy:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pki/tls/openssl.cnf [ crypto_policy ] .include /etc/crypto-policies/back-ends/opensslcnf.config
Configure OpenSSL library to use TLS Encryptionxccdf_org.ssgproject.content_rule_configure_openssl_tls_crypto_policy mediumCCE-84255-9

Configure OpenSSL library to use TLS Encryption

Rule IDxccdf_org.ssgproject.content_rule_configure_openssl_tls_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_openssl_tls_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-84255-9

References:
nistAC-17(2)
os-srgSRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174
stigidRHEL-08-010294
stigrefSV-230255r1017075_rule
Description
Crypto Policies are means of enforcing certain cryptographic settings for selected applications including OpenSSL. OpenSSL is by default configured to modify its configuration based on currently configured Crypto Policy. Editing the Crypto Policy back-end is not recommended. Check the crypto-policies(7) man page and choose a policy that configures TLS protocol to version 1.2 or higher, for example DEFAULT, FUTURE or FIPS policy. Or create and apply a custom policy that restricts minimum TLS version to 1.2. For example for versions prior to crypto-policies-20210617-1.gitc776d3e.el8.noarch this is expected:
$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config

MinProtocol = TLSv1.2
Or for version crypto-policies-20210617-1.gitc776d3e.el8.noarch and newer this is expected:
$ sudo grep -i MinProtocol /etc/crypto-policies/back-ends/opensslcnf.config

TLS.MinProtocol = TLSv1.2
DTLS.MinProtocol = DTLSv1.2
Rationale
Without cryptographic integrity protections, information can be altered by unauthorized users without detection.
Warnings
warning  This rule doesn't come with a remediation, automatically changing the crypto-policies may be too disruptive. Ensure the variable xccdf_org.ssgproject.content_value_var_system_crypto_policy is set to a Crypto Policy that satisfies OpenSSL minimum TLS protocol version 1.2. Custom policies may be applied too.
OVAL test results details

Check that the SSH configuration mandates usage of system-wide crypto policies.  oval:ssg-test_configure_openssl_tls_crypto_policy:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/crypto-policies/back-ends/opensslcnf.configTLS.MinProtocol = TLSv1.2

Installed version of crypto-policies is older than 20210617-1  oval:ssg-test_installed_version_of_crypto_policies:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
falsecrypto-policiesnoarch(none)1.git3177e06.el8202307310:20230731-1.git3177e06.el8199e2f91fd431d51crypto-policies-0:20230731-1.git3177e06.el8.noarch

Check that the SSH configuration mandates usage of system-wide crypto policies.  oval:ssg-test_configure_openssl_dtls_crypto_policy:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/crypto-policies/back-ends/opensslcnf.configDTLS.MinProtocol = DTLSv1.2
Configure SSH to use System Crypto Policyxccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy mediumCCE-80939-2

Configure SSH to use System Crypto Policy

Rule IDxccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-configure_ssh_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80939-2

References:
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.312(e)(1), 164.312(e)(2)(ii)
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1, CIP-007-3 R7.1
nistAC-17(a), AC-17(2), CM-6(a), MA-4(6), SC-13
osppFCS_SSH_EXT.1, FCS_SSHS_EXT.1, FCS_SSHC_EXT.1
pcidssReq-2.2
os-srgSRG-OS-000250-GPOS-00093
cis4.2.22
pcidss42.2.7, 2.2
stigidRHEL-08-010287
stigrefSV-244526r1017332_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. SSH is supported by crypto policy, but the SSH configuration may be set up to ignore it. To check that Crypto Policies settings are configured correctly, ensure that the CRYPTO_POLICY variable is either commented or not set at all in the /etc/sysconfig/sshd.
Rationale
Overriding the system crypto policy makes the behavior of the SSH service violate expectations, and makes system configuration more fragmented.
OVAL test results details

Check that the SSH configuration mandates usage of system-wide crypto policies.  oval:ssg-test_configure_ssh_crypto_policy:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_configure_ssh_crypto_policy:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sysconfig/sshd^\s*(?i)CRYPTO_POLICY\s*=.*$1
Configure SSH Client to Use FIPS 140 Validated Ciphers: openssh.configxccdf_org.ssgproject.content_rule_harden_sshd_ciphers_openssh_conf_crypto_policy highCCE-85902-5

Configure SSH Client to Use FIPS 140 Validated Ciphers: openssh.config

Rule IDxccdf_org.ssgproject.content_rule_harden_sshd_ciphers_openssh_conf_crypto_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-harden_sshd_ciphers_openssh_conf_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-85902-5

References:
nistAC-17(2)
os-srgSRG-OS-000033-GPOS-00014, SRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174, SRG-OS-000423-GPOS-00187
stigidRHEL-08-010020, RHEL-08-010296
stigrefSV-230223r1069327_rule, SV-272482r1069414_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be set up incorrectly. To check that Crypto Policies settings for ciphers are configured correctly, ensure that /etc/crypto-policies/back-ends/openssh.config contains the following line and is not commented out:
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
Rationale
Overriding the system crypto policy makes the behavior of the OpenSSH client violate expectations, and makes system configuration more fragmented. By specifying a cipher list with the order of ciphers being in a “strongest to weakest” orientation, the system will automatically attempt to use the strongest cipher for securing SSH connections.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.


sshd_approved_ciphers='aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr'


if [ -e "/etc/crypto-policies/back-ends/openssh.config" ] ; then
    
    LC_ALL=C sed -i "/^.*Ciphers\s\+/d" "/etc/crypto-policies/back-ends/openssh.config"
else
    touch "/etc/crypto-policies/back-ends/openssh.config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/crypto-policies/back-ends/openssh.config"

cp "/etc/crypto-policies/back-ends/openssh.config" "/etc/crypto-policies/back-ends/openssh.config.bak"
# Insert at the end of the file
printf '%s\n' "Ciphers ${sshd_approved_ciphers}" >> "/etc/crypto-policies/back-ends/openssh.config"
# Clean up after ourselves.
rm "/etc/crypto-policies/back-ends/openssh.config.bak"

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: XCCDF Value sshd_approved_ciphers # promote to variable
  set_fact:
    sshd_approved_ciphers: !!str aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
  tags:
    - always

- name: 'Configure SSH Daemon to Use FIPS 140-2 Validated Ciphers: openssh.config'
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*Ciphers\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/crypto-policies/back-ends/openssh.config
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*Ciphers\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/crypto-policies/back-ends/openssh.config
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*Ciphers\s+
      line: Ciphers {{ sshd_approved_ciphers }}
      state: present
  tags:
  - CCE-85902-5
  - DISA-STIG-RHEL-08-010020
  - DISA-STIG-RHEL-08-010296
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_openssh_conf_crypto_policy
  - high_severity
  - low_complexity
  - low_disruption
  - reboot_required
  - restrict_strategy
OVAL test results details

test the value of Ciphers setting in the /etc/crypto-policies/back-ends/openssh.config file  oval:ssg-test_harden_sshd_ciphers_openssh_conf_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/back-ends/openssh.configCiphers aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc
Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.configxccdf_org.ssgproject.content_rule_harden_sshd_ciphers_opensshserver_conf_crypto_policy mediumCCE-85897-7

Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config

Rule IDxccdf_org.ssgproject.content_rule_harden_sshd_ciphers_opensshserver_conf_crypto_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-harden_sshd_ciphers_opensshserver_conf_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85897-7

References:
nistAC-17(2)
os-srgSRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093
stigidRHEL-08-010291
stigrefSV-230252r1067104_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be set up incorrectly. To check that Crypto Policies settings for ciphers are configured correctly, ensure that /etc/crypto-policies/back-ends/opensshserver.config contains the following text and is not commented out:
-oCiphers=aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
Rationale
Overriding the system crypto policy makes the behavior of the OpenSSH server violate expectations, and makes system configuration more fragmented. By specifying a cipher list with the order of ciphers being in a “strongest to weakest” orientation, the system will automatically attempt to use the strongest cipher for securing SSH connections.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.


sshd_approved_ciphers='aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr'


CONF_FILE=/etc/crypto-policies/back-ends/opensshserver.config
LOCAL_CONF_DIR=/etc/crypto-policies/local.d
LOCAL_CONF_FILE=${LOCAL_CONF_DIR}/opensshserver-ssg.config
correct_value="-oCiphers=${sshd_approved_ciphers}"

# Test if file exists, create default it if not
if [[ ! -s ${CONF_FILE} ]] || ! grep -q "^\s*CRYPTO_POLICY=" ${CONF_FILE} ; then
    update-crypto-policies --no-reload # Generate a default configuration
fi

# Get the last occurrence of CRYPTO_POLICY
last_crypto_policy=$(grep -Eo "^\s*CRYPTO_POLICY='[^']+'" ${CONF_FILE} | tail -n 1)

# Copy the last CRYPTO_POLICY value to the local configuration file
if [[ -n "$last_crypto_policy" ]]; then
    if ! grep -qe "$correct_value" <<< "$last_crypto_policy"; then
        # If an existing -oCiphers= is found, replace it
        # Else, append correct_value before the closing apostrophe
        if [[ "$last_crypto_policy" == *"-oCiphers="* ]]; then
            last_crypto_policy=$(echo "$last_crypto_policy" | sed -E "s/-oCiphers=\S+/${correct_value}/")
        else
            last_crypto_policy=$(echo "$last_crypto_policy" | sed -E "s/'[[:space:]]*$/ ${correct_value}'/")
        fi
        # Write updated line to LOCAL_CONF_FILE
        echo -e "\n$last_crypto_policy" > "$LOCAL_CONF_FILE"
    fi
else
    echo -e "\nCRYPTO_POLICY='${correct_value}'" > ${LOCAL_CONF_FILE}
fi

update-crypto-policies --no-reload

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: XCCDF Value sshd_approved_ciphers # promote to variable
  set_fact:
    sshd_approved_ciphers: !!str aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
  tags:
    - always

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Set relevant paths and correct value'
  ansible.builtin.set_fact:
    opensshserver_path: /etc/crypto-policies/back-ends/opensshserver.config
    local_path: /etc/crypto-policies/local.d/opensshserver-ssg.config
    correct_value: -oCiphers={{ sshd_approved_ciphers }}
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Ensure crypto config exists'
  ansible.builtin.stat:
    path: '{{ opensshserver_path }}'
    follow: true
  register: opensshserver_file
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Generate default config if missing or empty'
  ansible.builtin.command: update-crypto-policies --no-reload
  when: not opensshserver_file.stat.exists or opensshserver_file.stat.size == 0
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Read opensshserver.config content'
  ansible.builtin.slurp:
    src: '{{ opensshserver_path }}'
  register: ssh_config_raw
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Extract last CRYPTO_POLICY line'
  ansible.builtin.set_fact:
    last_crypto_policy: '{{ (ssh_config_raw.content | b64decode).splitlines() | select(''match'',
      "^\s*CRYPTO_POLICY=''[^'']+''") | list | last | default('''') }}'
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Check if correct_value is present'
  ansible.builtin.set_fact:
    cipher_is_correct: '{{ correct_value in last_crypto_policy }}'
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Extract current Ciphers if needed'
  ansible.builtin.set_fact:
    existing_cipher: '{{ (last_crypto_policy | regex_findall(''(-oCiphers=\S+)'',
      ''\1'')) | last | default('''') }}'
  when: not cipher_is_correct and last_crypto_policy != ''
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Build full updated CRYPTO_POLICY line'
  ansible.builtin.set_fact:
    updated_crypto_policy: '{% if last_crypto_policy == '''' %} CRYPTO_POLICY=''{{
      correct_value }}'' {% elif existing_cipher != '''' %} {{ last_crypto_policy
      | regex_replace(existing_cipher, correct_value) }} {% else %} {{ last_crypto_policy[:-1]
      ~ " " ~ correct_value ~ "''" }} {% endif %}'
  when: not cipher_is_correct
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Ensure local.d dir exists'
  ansible.builtin.file:
    path: '{{ local_path | dirname }}'
    state: directory
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Write CRYPTO_POLICY to local config'
  ansible.builtin.lineinfile:
    path: '{{ local_path }}'
    line: |-
      {{ '
      ' ~ updated_crypto_policy }}
    create: true
    insertafter: EOF
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated Ciphers: opensshserver.config:
    Apply updated crypto policies'
  ansible.builtin.command: update-crypto-policies --no-reload
  tags:
  - CCE-85897-7
  - DISA-STIG-RHEL-08-010291
  - NIST-800-53-AC-17(2)
  - harden_sshd_ciphers_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

test the value of Ciphers setting in the /etc/crypto-policies/back-ends/opensshserver.config file  oval:ssg-test_harden_sshd_ciphers_opensshserver_conf_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/back-ends/opensshserver.configCRYPTO_POLICY='-oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc -oMACs=hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512 -oGSSAPIKexAlgorithms=gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-,gss-gex-sha1-,gss-group14-sha1- -oKexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oPubkeyAcceptedKeyTypes=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oCASignatureAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa'
Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.configxccdf_org.ssgproject.content_rule_harden_sshd_macs_openssh_conf_crypto_policy mediumCCE-85870-4

Configure SSH Client to Use FIPS 140-2 Validated MACs: openssh.config

Rule IDxccdf_org.ssgproject.content_rule_harden_sshd_macs_openssh_conf_crypto_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-harden_sshd_macs_openssh_conf_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85870-4

References:
nistAC-17(2)
os-srgSRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093
stigidRHEL-08-010020, RHEL-08-010296
stigrefSV-230223r1069327_rule, SV-272482r1069414_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be set up incorrectly. To check that Crypto Policies settings are configured correctly, ensure that /etc/crypto-policies/back-ends/openssh.config contains the following line and is not commented out: MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
Rationale
Overriding the system crypto policy makes the behavior of the OpenSSH client violate expectations, and makes system configuration more fragmented.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.


sshd_approved_macs='hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256'


if [ -e "/etc/crypto-policies/back-ends/openssh.config" ] ; then
    
    LC_ALL=C sed -i "/^.*MACs\s\+/d" "/etc/crypto-policies/back-ends/openssh.config"
else
    touch "/etc/crypto-policies/back-ends/openssh.config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/crypto-policies/back-ends/openssh.config"

cp "/etc/crypto-policies/back-ends/openssh.config" "/etc/crypto-policies/back-ends/openssh.config.bak"
# Insert at the end of the file
printf '%s\n' "MACs ${sshd_approved_macs}" >> "/etc/crypto-policies/back-ends/openssh.config"
# Clean up after ourselves.
rm "/etc/crypto-policies/back-ends/openssh.config.bak"

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: XCCDF Value sshd_approved_macs # promote to variable
  set_fact:
    sshd_approved_macs: !!str hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
  tags:
    - always

- name: 'Configure SSH Daemon to Use FIPS 140-2 Validated MACs: openssh.config'
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*MACs\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/crypto-policies/back-ends/openssh.config
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*MACs\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/crypto-policies/back-ends/openssh.config
    ansible.builtin.lineinfile:
      path: /etc/crypto-policies/back-ends/openssh.config
      create: true
      regexp: (?i)^.*MACs\s+
      line: MACs {{ sshd_approved_macs }}
      state: present
  tags:
  - CCE-85870-4
  - DISA-STIG-RHEL-08-010020
  - DISA-STIG-RHEL-08-010296
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_openssh_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

test the value of MACs setting in the /etc/crypto-policies/back-ends/openssh.config file  oval:ssg-test_harden_sshd_macs_openssh_conf_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/back-ends/openssh.configMACs hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.configxccdf_org.ssgproject.content_rule_harden_sshd_macs_opensshserver_conf_crypto_policy mediumCCE-85899-3

Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config

Rule IDxccdf_org.ssgproject.content_rule_harden_sshd_macs_opensshserver_conf_crypto_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-harden_sshd_macs_opensshserver_conf_crypto_policy:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85899-3

References:
nistAC-17(2)
os-srgSRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093
stigidRHEL-08-010290
stigrefSV-230251r1044814_rule
Description
Crypto Policies provide a centralized control over crypto algorithms usage of many packages. OpenSSH is supported by system crypto policy, but the OpenSSH configuration may be set up incorrectly. To check that Crypto Policies settings are configured correctly, ensure that /etc/crypto-policies/back-ends/opensshserver.config contains the following text and is not commented out: -oMACS=hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
Rationale
Overriding the system crypto policy makes the behavior of the OpenSSH server violate expectations, and makes system configuration more fragmented.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  System Crypto Modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.


sshd_approved_macs='hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256'


CONF_FILE=/etc/crypto-policies/back-ends/opensshserver.config
LOCAL_CONF_DIR=/etc/crypto-policies/local.d
LOCAL_CONF_FILE=${LOCAL_CONF_DIR}/opensshserver-ssg.config
correct_value="-oMACs=${sshd_approved_macs}"

# Test if file exists, create default it if not
if [[ ! -s ${CONF_FILE} ]] || ! grep -q "^\s*CRYPTO_POLICY=" ${CONF_FILE} ; then
    update-crypto-policies --no-reload # Generate a default configuration
fi

# Get the last occurrence of CRYPTO_POLICY
last_crypto_policy=$(grep -Eo "^\s*CRYPTO_POLICY='[^']+'" ${CONF_FILE} | tail -n 1)

# Copy the last CRYPTO_POLICY value to the local configuration file
if [[ -n "$last_crypto_policy" ]]; then
    if ! grep -qe "$correct_value" <<< "$last_crypto_policy"; then
        # If an existing -oMACs= is found, replace it
        # Else, append correct_value before the closing apostrophe
        if [[ "$last_crypto_policy" == *"-oMACs="* ]]; then
            last_crypto_policy=$(echo "$last_crypto_policy" | sed -E "s/-oMACs=\S+/${correct_value}/")
        else
            last_crypto_policy=$(echo "$last_crypto_policy" | sed -E "s/'[[:space:]]*$/ ${correct_value}'/")
        fi
        # Write updated line to LOCAL_CONF_FILE
        echo -e "\n$last_crypto_policy" > "$LOCAL_CONF_FILE"
    fi
else
    echo -e "\nCRYPTO_POLICY='${correct_value}'" > ${LOCAL_CONF_FILE}
fi

update-crypto-policies --no-reload

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: XCCDF Value sshd_approved_macs # promote to variable
  set_fact:
    sshd_approved_macs: !!str hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
  tags:
    - always

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Set relevant paths and correct value'
  ansible.builtin.set_fact:
    opensshserver_path: /etc/crypto-policies/back-ends/opensshserver.config
    local_path: /etc/crypto-policies/local.d/opensshserver-ssg.config
    correct_value: -oMACs={{ sshd_approved_macs }}
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Ensure crypto config exists'
  ansible.builtin.stat:
    path: '{{ opensshserver_path }}'
    follow: true
  register: opensshserver_file
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Generate default config if missing or empty'
  ansible.builtin.command: update-crypto-policies --no-reload
  when: not opensshserver_file.stat.exists or opensshserver_file.stat.size == 0
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Read opensshserver.config content'
  ansible.builtin.slurp:
    src: '{{ opensshserver_path }}'
  register: ssh_config_raw
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Extract last CRYPTO_POLICY line'
  ansible.builtin.set_fact:
    last_crypto_policy: '{{ (ssh_config_raw.content | b64decode).splitlines() | select(''match'',
      "^\s*CRYPTO_POLICY=''[^'']+''") | list | last | default('''') }}'
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Check if correct_value is present'
  ansible.builtin.set_fact:
    mac_is_correct: '{{ correct_value in last_crypto_policy }}'
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Extract current Ciphers if needed'
  ansible.builtin.set_fact:
    existing_mac: '{{ (last_crypto_policy | regex_findall(''(-oMACs=\S+)'', ''\1''))
      | last | default('''') }}'
  when: not mac_is_correct and last_crypto_policy != ''
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Build full updated CRYPTO_POLICY line'
  ansible.builtin.set_fact:
    updated_crypto_policy: '{% if last_crypto_policy == '''' %} CRYPTO_POLICY=''{{
      correct_value }}'' {% elif existing_mac != '''' %} {{ last_crypto_policy | regex_replace(existing_mac,
      correct_value) }} {% else %} {{ last_crypto_policy[:-1] ~ " " ~ correct_value
      ~ "''" }} {% endif %}'
  when: not mac_is_correct
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Ensure local.d dir exists'
  ansible.builtin.file:
    path: '{{ local_path | dirname }}'
    state: directory
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Write CRYPTO_POLICY to local config'
  ansible.builtin.lineinfile:
    path: '{{ local_path }}'
    line: |-
      {{ '
      ' ~ updated_crypto_policy }}
    create: true
    insertafter: EOF
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: 'Configure SSH Server to Use FIPS 140-2 Validated MACs: opensshserver.config:
    Apply updated crypto policies'
  ansible.builtin.command: update-crypto-policies --no-reload
  tags:
  - CCE-85899-3
  - DISA-STIG-RHEL-08-010290
  - NIST-800-53-AC-17(2)
  - harden_sshd_macs_opensshserver_conf_crypto_policy
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

test the value of MACs setting in the /etc/crypto-policies/back-ends/opensshserver.config file  oval:ssg-test_harden_sshd_macs_opensshserver_conf_crypto_policy:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/back-ends/opensshserver.configCRYPTO_POLICY='-oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc -oMACs=hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512 -oGSSAPIKexAlgorithms=gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-,gss-gex-sha1-,gss-group14-sha1- -oKexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oPubkeyAcceptedKeyTypes=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oCASignatureAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa'
The Installed Operating System Is Vendor Supportedxccdf_org.ssgproject.content_rule_installed_OS_is_vendor_supported highCCE-80947-5

The Installed Operating System Is Vendor Supported

Rule IDxccdf_org.ssgproject.content_rule_installed_OS_is_vendor_supported
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-installed_OS_is_vendor_supported:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80947-5

References:
cis-csc18, 20, 4
cobit5APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02
isa-62443-20094.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9
iso27001-2013A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3
nistCM-6(a), MA-6, SA-13(a)
nist-csfID.RA-1, PR.IP-12
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010000
stigrefSV-230221r1017040_rule
Description
The installed operating system must be maintained by a vendor. Red Hat Enterprise Linux is supported by Red Hat, Inc. As the Red Hat Enterprise Linux vendor, Red Hat, Inc. is responsible for providing security patches.
Rationale
An operating system is considered "supported" if the vendor continues to provide security patches for the product. With an unsupported release, it will not be possible to resolve any security issue discovered in the system software.
Warnings
warning  There is no remediation besides switching to a different operating system.
OVAL test results details

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

/etc/almalinux-release exists  oval:ssg-test_almalinux:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_almalinux:obj:1 of type file_object
Filepath
/etc/almalinux-release

Check Custom OS version  oval:ssg-test_almalinux9:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_almalinux9:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/almalinux-release^AlmaLinux release 9.[0-9]+ .*$1

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

installed OS part of unix family  oval:ssg-test_rhel9_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 9  oval:ssg-test_rhel9:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
falseredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 9  oval:ssg-test_ol9_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol9_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 9  oval:ssg-test_rhevh_rhel9_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel9_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

installed OS part of unix family  oval:ssg-test_rhel10_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 10  oval:ssg-test_rhel10:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
falseredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 10  oval:ssg-test_rhevh_rhel10_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel10_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 7  oval:ssg-test_ol7_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol7_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 9  oval:ssg-test_ol9_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol9_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

installed OS part of unix family  oval:ssg-test_sle12_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

sled-release is version 6  oval:ssg-test_sle12_desktop:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sle12_desktop:obj:1 of type rpminfo_object
Name
sled-release

sles-release is version 6  oval:ssg-test_sle12_server:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sle12_server:obj:1 of type rpminfo_object
Name
sles-release

SLES_SAP-release is version 12  oval:ssg-test_sles_12_for_sap:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sles_12_for_sap:obj:1 of type rpminfo_object
Name
SLES_SAP-release

installed OS part of unix family  oval:ssg-test_sle15_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

sled-release is version 15  oval:ssg-test_sle15_desktop:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sle15_desktop:obj:1 of type rpminfo_object
Name
sled-release

sles-release is version 15  oval:ssg-test_sle15_server:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sle15_server:obj:1 of type rpminfo_object
Name
sles-release

SLES_SAP-release is version 15  oval:ssg-test_sles_15_for_sap:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sles_15_for_sap:obj:1 of type rpminfo_object
Name
SLES_SAP-release

SUMA is version 4  oval:ssg-test_suma_4:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_suma_4:obj:1 of type rpminfo_object
Name
SUSE-Manager-Server-release

SLE HPC release is version 15  oval:ssg-test_sle_hpc:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sle_hpc:obj:1 of type rpminfo_object
Name
SLE_HPC-release

installed OS part of unix family  oval:ssg-test_slmicro5_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

sle-micro-release is version 5  oval:ssg-test_slmicroos5:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_slmicroos5:obj:1 of type rpminfo_object
Name
SUSE-MicroOS-release

sle-micro-release is version 5  oval:ssg-test_slmicro5:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_slmicro5:obj:1 of type rpminfo_object
Name
SLE-Micro-release

installed OS part of unix family  oval:ssg-test_slmicro6_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

sle-micro-release is version 6  oval:ssg-test_slmicro6:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_slmicro6:obj:1 of type rpminfo_object
Name
SL-Micro-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

/etc/lsb-release exists  oval:ssg-test_lsb:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_lsb:obj:1 of type file_object
Filepath
/etc/lsb-release

Check Ubuntu  oval:ssg-test_ubuntu:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ubuntu:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/lsb-release^DISTRIB_ID=Ubuntu$1

Check Ubuntu version  oval:ssg-test_ubuntu_noble:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ubuntu_noble:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/lsb-release^DISTRIB_CODENAME=noble$1
Encrypt Partitionsxccdf_org.ssgproject.content_rule_encrypt_partitions highCCE-80789-1

Encrypt Partitions

Rule IDxccdf_org.ssgproject.content_rule_encrypt_partitions
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80789-1

References:
cis-csc13, 14
cobit5APO01.06, BAI02.01, BAI06.01, DSS04.07, DSS05.03, DSS05.04, DSS05.07, DSS06.02, DSS06.06
cui3.13.16
hipaa164.308(a)(1)(ii)(D), 164.308(b)(1), 164.310(d), 164.312(a)(1), 164.312(a)(2)(iii), 164.312(a)(2)(iv), 164.312(b), 164.312(c), 164.314(b)(2)(i), 164.312(d)
isa-62443-2013SR 3.4, SR 4.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R4.2, CIP-007-3 R5.1
nistCM-6(a), SC-28, SC-28(1), SC-13, AU-9(3)
nist-csfPR.DS-1, PR.DS-5
os-srgSRG-OS-000405-GPOS-00184, SRG-OS-000185-GPOS-00079, SRG-OS-000404-GPOS-00183
stigidRHEL-08-010030
stigrefSV-230224r1044787_rule
Description
Red Hat Enterprise Linux 8 natively supports partition encryption through the Linux Unified Key Setup-on-disk-format (LUKS) technology. The easiest way to encrypt a partition is during installation time.

For manual installations, select the Encrypt checkbox during partition creation to encrypt the partition. When this option is selected the system will prompt for a passphrase to use in decrypting the partition. The passphrase will subsequently need to be entered manually every time the system boots.

For automated/unattended installations, it is possible to use Kickstart by adding the --encrypted and --passphrase= options to the definition of each partition to be encrypted. For example, the following line would encrypt the root partition:
part / --fstype=ext4 --size=100 --onpart=hda1 --encrypted --passphrase=PASSPHRASE
Any PASSPHRASE is stored in the Kickstart in plaintext, and the Kickstart must then be protected accordingly. Omitting the --passphrase= option from the partition definition will cause the installer to pause and interactively ask for the passphrase during installation.

By default, the Anaconda installer uses aes-xts-plain64 cipher with a minimum 512 bit key size which should be compatible with FIPS enabled.

Detailed information on encrypting partitions using LUKS or LUKS ciphers can be found on the Red Hat Enterprise Linux 8 Documentation web site:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/encrypting-block-devices-using-luks_security-hardening .
Rationale
The risk of a system's physical compromise, particularly mobile systems such as laptops, places its data at risk of compromise. Encrypting this data mitigates the risk of its loss if the system is lost.
Evaluation messages
info 
No candidate or applicable check found.
Ensure /home Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_home lowCCE-81044-0

Ensure /home Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_home
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_home:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-81044-0

References:
cis-csc12, 15, 8
cobit5APO13.01, DSS05.02
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.13.1.1, A.13.2.1, A.14.1.3
nistCM-6(a), SC-5(2)
nist-csfPR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR28
cis1.1.2.3.1
stigidRHEL-08-010800
stigrefSV-230328r1017139_rule
Description
If user home directories will be stored locally, create a separate partition for /home at installation time (or migrate it later using LVM). If /home will be mounted from another system such as an NFS server, then creating a separate partition is not necessary at installation time, and the mountpoint can instead be configured later.
Rationale
Ensuring that /home is mounted on its own partition enables the setting of more restrictive mount options, and also helps ensure that users cannot trivially fill partitions used for log or audit data storage.
OVAL test results details

/home on own partition  oval:ssg-testhome_partition:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866
Ensure /tmp Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_tmp lowCCE-80851-9

Ensure /tmp Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_tmp
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_tmp:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-80851-9

References:
cis-csc12, 15, 8
cobit5APO13.01, DSS05.02
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.13.1.1, A.13.2.1, A.14.1.3
nistCM-6(a), SC-5(2)
nist-csfPR.PT-4
os-srgSRG-OS-000480-GPOS-00227
cis1.1.2.1.1
stigidRHEL-08-010543
stigrefSV-230295r1017106_rule
Description
The /tmp directory is a world-writable directory used for temporary file storage. Ensure it has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale
The /tmp partition is used as temporary storage by many programs. Placing /tmp in its own partition enables the setting of more restrictive mount options, which can help protect programs which use it.

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /tmp


[[customizations.filesystem]]
mountpoint = "/tmp"
size = 1073741824


logvol /tmp 1024
OVAL test results details

/tmp on own partition  oval:ssg-testtmp_partition:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_mounttmp_own_partition:obj:1 of type partition_object
Mount point
/tmp
Ensure /var Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_var lowCCE-80852-7

Ensure /var Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_var
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_var:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-80852-7

References:
cis-csc12, 15, 8
cobit5APO13.01, DSS05.02
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.13.1.1, A.13.2.1, A.14.1.3
nistCM-6(a), SC-5(2)
nist-csfPR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR28
cis1.1.2.4.1
stigidRHEL-08-010540
stigrefSV-230292r1017103_rule
Description
The /var directory is used by daemons and other system services to store frequently-changing data. Ensure that /var has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale
Ensuring that /var is mounted on its own partition enables the setting of more restrictive mount options. This helps protect system services such as daemons or other programs which use it. It is not uncommon for the /var directory to contain world-writable directories installed by other software packages.

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /var


[[customizations.filesystem]]
mountpoint = "/var"
size = 3221225472


logvol /var 3072
OVAL test results details

/var on own partition  oval:ssg-testvar_partition:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_mountvar_own_partition:obj:1 of type partition_object
Mount point
/var
Ensure /var/log Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_var_log lowCCE-80853-5

Ensure /var/log Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_var_log
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_var_log:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-80853-5

References:
cis-csc1, 12, 14, 15, 16, 3, 5, 6, 8
cobit5APO11.04, APO13.01, BAI03.05, DSS05.02, DSS05.04, DSS05.07, MEA02.01
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3
nerc-cipCIP-007-3 R6.5
nistCM-6(a), AU-4, SC-5(2)
nist-csfPR.PT-1, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR28
cis1.1.2.6.1
stigidRHEL-08-010541
stigrefSV-230293r1017104_rule
Description
System logs are stored in the /var/log directory. Ensure that /var/log has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale
Placing /var/log in its own partition enables better separation between log files and other files in /var/.

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /var/log


[[customizations.filesystem]]
mountpoint = "/var/log"
size = 1073741824


logvol /var/log 1024
OVAL test results details

/var/log on own partition  oval:ssg-testvar_log_partition:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_mountvar_log_own_partition:obj:1 of type partition_object
Mount point
/var/log
Ensure /var/log/audit Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_var_log_audit lowCCE-80854-3

Ensure /var/log/audit Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_var_log_audit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_var_log_audit:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-80854-3

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 8
cobit5APO11.04, APO13.01, BAI03.05, BAI04.04, DSS05.02, DSS05.04, DSS05.07, MEA02.01
hipaa164.312(a)(2)(ii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.17.2.1
nerc-cipCIP-007-3 R6.5
nistCM-6(a), AU-4, SC-5(2)
nist-csfPR.DS-4, PR.PT-1, PR.PT-4
osppFMT_SMF_EXT.1
os-srgSRG-OS-000341-GPOS-00132, SRG-OS-000480-GPOS-00227
app-srg-ctrSRG-APP-000357-CTR-000800
anssiR71
cis1.1.2.7.1
stigidRHEL-08-010542
stigrefSV-230294r1017105_rule
Description
Audit logs are stored in the /var/log/audit directory. Ensure that /var/log/audit has its own partition or logical volume at installation time, or migrate it using LVM. Make absolutely certain that it is large enough to store all audit logs that will be created by the auditing daemon.
Rationale
Placing /var/log/audit in its own partition enables better separation between audit files and other files, and helps ensure that auditing cannot be halted due to the partition running out of space.

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /var/log/audit


[[customizations.filesystem]]
mountpoint = "/var/log/audit"
size = 10737418240


logvol /var/log/audit 10240
OVAL test results details

/var/log/audit on own partition  oval:ssg-testvar_log_audit_partition:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_mountvar_log_audit_own_partition:obj:1 of type partition_object
Mount point
/var/log/audit
Ensure /var/tmp Located On Separate Partitionxccdf_org.ssgproject.content_rule_partition_for_var_tmp mediumCCE-82730-3

Ensure /var/tmp Located On Separate Partition

Rule IDxccdf_org.ssgproject.content_rule_partition_for_var_tmp
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-partition_for_var_tmp:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82730-3

References:
os-srgSRG-OS-000480-GPOS-00227
anssiR28
cis1.1.2.5.1
stigidRHEL-08-010544
stigrefSV-244529r1017336_rule
Description
The /var/tmp directory is a world-writable directory used for temporary file storage. Ensure it has its own partition or logical volume at installation time, or migrate it using LVM.
Rationale
The /var/tmp partition is used as temporary storage by many programs. Placing /var/tmp in its own partition enables the setting of more restrictive mount options, which can help protect programs which use it.

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /var/tmp


[[customizations.filesystem]]
mountpoint = "/var/tmp"
size = 1073741824


logvol /var/tmp 1024
OVAL test results details

/var/tmp on own partition  oval:ssg-testvar_tmp_partition:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_mountvar_tmp_own_partition:obj:1 of type partition_object
Mount point
/var/tmp
Disable the GNOME3 Login User Listxccdf_org.ssgproject.content_rule_dconf_gnome_disable_user_list mediumCCE-86195-5

Disable the GNOME3 Login User List

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_disable_user_list
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_disable_user_list:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86195-5

References:
nistCM-6(a), AC-23
os-srgSRG-OS-000480-GPOS-00227
cis1.8.3
stigidRHEL-08-020032
stigrefSV-244536r1017343_rule
Description
In the default graphical environment, users logging directly into the system are greeted with a login screen that displays all known users. This functionality should be disabled by setting disable-user-list to true.

To disable, add or edit disable-user-list to /etc/dconf/db/gdm.d/00-security-settings. For example:
[org/gnome/login-screen]
disable-user-list=true
Once the setting has been added, add a lock to /etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/login-screen/disable-user-list
After the settings have been set, run dconf update.
Rationale
Leaving the user list enabled is a security risk since it allows anyone with physical access to the system to quickly enumerate known user accounts without logging in.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|gdm.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings"
DBDIR="/etc/dconf/db/gdm.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*disable-user-list\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)disable-user-list(\s*=)/#\1disable-user-list\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/login-screen\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*disable-user-list\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*disable-user-list\\s*=\\s*.*/disable-user-list=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/login-screen\\]|a\\disable-user-list=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/login-screen/disable-user-list$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|gdm.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/gdm.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/login-screen/disable-user-list$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/login-screen/disable-user-list$" /etc/dconf/db/gdm.d/
then
    echo "/org/gnome/login-screen/disable-user-list" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86195-5
  - DISA-STIG-RHEL-08-020032
  - NIST-800-53-AC-23
  - NIST-800-53-CM-6(a)
  - dconf_gnome_disable_user_list
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Disable the GNOME3 Login User List
  community.general.ini_file:
    dest: /etc/dconf/db/gdm.d/00-security-settings
    section: org/gnome/login-screen
    option: disable-user-list
    value: 'true'
    no_extra_spaces: true
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-86195-5
  - DISA-STIG-RHEL-08-020032
  - NIST-800-53-AC-23
  - NIST-800-53-CM-6(a)
  - dconf_gnome_disable_user_list
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME3 disablement of Login User List
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/login-screen/disable-user-list$
    line: /org/gnome/login-screen/disable-user-list
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-86195-5
  - DISA-STIG-RHEL-08-020032
  - NIST-800-53-AC-23
  - NIST-800-53-CM-6(a)
  - dconf_gnome_disable_user_list
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-86195-5
  - DISA-STIG-RHEL-08-020032
  - NIST-800-53-AC-23
  - NIST-800-53-CM-6(a)
  - dconf_gnome_disable_user_list
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

GUI user list is disabled  oval:ssg-test_disable_user_list:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_disable_user_list:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/gdm.d/^.*$^\[org/gnome/login-screen\]([^\n]*\n+)+?disable-user-list=true$1

GUI user list cannot be enabled  oval:ssg-test_prevent_user_disable_user_list:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_disable_user_list:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/gdm.d/locks/^.*$^/org/gnome/login-screen/disable-user-list$1
Enable the GNOME3 Screen Locking On Smartcard Removalxccdf_org.ssgproject.content_rule_dconf_gnome_lock_screen_on_smartcard_removal mediumCCE-83910-0

Enable the GNOME3 Screen Locking On Smartcard Removal

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_lock_screen_on_smartcard_removal
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_lock_screen_on_smartcard_removal:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-83910-0

References:
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011
stigidRHEL-08-020050
stigrefSV-230351r1017164_rule
Description
In the default graphical environment, screen locking on smartcard removal can be enabled by setting removal-action to 'lock-screen'.

To enable, add or edit removal-action to /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/settings-daemon/peripherals/smartcard]
removal-action='lock-screen'
Once the setting has been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/settings-daemon/peripherals/smartcard/removal-action
After the settings have been set, run dconf update.
Rationale
Locking the screen automatically when removing the smartcard can prevent undesired access to system.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/peripherals/smartcard\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*removal-action\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)removal-action(\s*=)/#\1removal-action\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/settings-daemon/peripherals/smartcard\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/settings-daemon/peripherals/smartcard]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "'lock-screen'")"
if grep -q "^\\s*removal-action\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*removal-action\\s*=\\s*.*/removal-action=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/settings-daemon/peripherals/smartcard\\]|a\\removal-action=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/settings-daemon/peripherals/smartcard/removal-action" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Detect if removal-action can be found on /etc/dconf/db/local.d/
  ansible.builtin.find:
    path: /etc/dconf/db/local.d/
    contains: ^\s*removal-action
  register: dconf_gnome_lock_screen_on_smartcard_removal_config_files
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Configure removal-action - default file
  community.general.ini_file:
    dest: /etc/dconf/db/local.d//00-security-settings
    section: org/gnome/settings-daemon/peripherals/smartcard
    option: removal-action
    value: '''lock-screen'''
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched
    == 0
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Configure removal-action - existing files
  community.general.ini_file:
    dest: '{{ item.path }}'
    section: org/gnome/settings-daemon/peripherals/smartcard
    option: removal-action
    value: '''lock-screen'''
    create: true
  with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_config_files.files
    }}'
  when:
  - '"gdm" in ansible_facts.packages'
  - dconf_gnome_lock_screen_on_smartcard_removal_config_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_config_files.matched
    > 0
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Detect if lock for removal-action can be found on /etc/dconf/db/local.d/
  ansible.builtin.find:
    path: /etc/dconf/db/local.d/locks
    contains: ^\s*removal-action
  register: dconf_gnome_lock_screen_on_smartcard_removal_lock_files
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification removal-action - default file
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$
    line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched
    == 0
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification removal-action - existing files
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$
    line: /org/gnome/settings-daemon/peripherals/smartcard/removal-action
    create: true
  with_items: '{{ dconf_gnome_lock_screen_on_smartcard_removal_lock_files.files }}'
  when:
  - '"gdm" in ansible_facts.packages'
  - dconf_gnome_lock_screen_on_smartcard_removal_lock_files is defined and dconf_gnome_lock_screen_on_smartcard_removal_lock_files.matched
    > 0
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update - removal-action
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-83910-0
  - DISA-STIG-RHEL-08-020050
  - dconf_gnome_lock_screen_on_smartcard_removal
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

tests the value of removal-action setting in the /etc/dconf/db/local.d/ file  oval:ssg-test_dconf_gnome_lock_screen_on_smartcard_removal:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_dconf_gnome_lock_screen_on_smartcard_removal:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^\s*\[org/gnome/settings-daemon/peripherals/smartcard\].*(?:\n\s*[^[\s].*)*\n^\s*removal-action[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#)1

Prevent user from modifying removal-action  oval:ssg-test_prevent_user_removal-action:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_removal-action:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks^.*$^/org/gnome/settings-daemon/peripherals/smartcard/removal-action$1
Set GNOME3 Screensaver Inactivity Timeoutxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay mediumCCE-80775-0

Set GNOME3 Screensaver Inactivity Timeout

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_screensaver_idle_delay:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80775-0

References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-11(a), CM-6(a)
nist-csfPR.AC-7
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.4
pcidss48.2.8, 8.2
stigidRHEL-08-020060
stigrefSV-230352r1017165_rule
Description
The idle time-out value for inactivity in the GNOME3 desktop is configured via the idle-delay setting must be set under an appropriate configuration file(s) in the /etc/dconf/db/local.d directory and locked in /etc/dconf/db/local.d/locks directory to prevent user modification.

For example, to configure the system for a 15 minute delay, add the following to /etc/dconf/db/local.d/00-security-settings:
[org/gnome/desktop/session]
idle-delay=uint32 900
Rationale
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME3 can be configured to identify when a user's session has idled and take action to initiate a session lock.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

inactivity_timeout_value='900'


# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)idle-delay(\s*=)/#\1idle-delay\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/session\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")"
if grep -q "^\\s*idle-delay\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${DCONFFILE}"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80775-0
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020060
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
- name: XCCDF Value inactivity_timeout_value # promote to variable
  set_fact:
    inactivity_timeout_value: !!str 900
  tags:
    - always

- name: Set GNOME3 Screensaver Inactivity Timeout
  community.general.ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/session
    option: idle-delay
    value: uint32 {{ inactivity_timeout_value }}
    create: true
    no_extra_spaces: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80775-0
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020060
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80775-0
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020060
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_idle_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

screensaver idle delay is configured  oval:ssg-test_screensaver_idle_delay:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_screensaver_idle_delay:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^\[org/gnome/desktop/session\]([^\n]*\n+)+?idle-delay=uint32[\s][0-9]*$1

screensaver idle delay setting is correct  oval:ssg-test_screensaver_idle_delay_setting:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_screensaver_idle_delay_setting:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^idle-delay[\s=]*uint32[\s]([^=\s]*)1
Set GNOME3 Screensaver Lock Delay After Activation Periodxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay mediumCCE-80776-8

Set GNOME3 Screensaver Lock Delay After Activation Period

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_screensaver_lock_delay:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80776-8

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-11(a), CM-6(a)
nist-csfPR.AC-7
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.4
pcidss48.2.8, 8.2
stigidRHEL-08-020031
stigrefSV-244535r1017342_rule
Description
To activate the locking delay of the screensaver in the GNOME3 desktop when the screensaver is activated, add or set lock-delay to uint32 5 in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
lock-delay=uint32 5
After the settings have been set, run dconf update.
Rationale
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

var_screensaver_lock_delay='5'


# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*lock-delay\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)lock-delay(\s*=)/#\1lock-delay\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${var_screensaver_lock_delay}")"
if grep -q "^\\s*lock-delay\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*lock-delay\\s*=\\s*.*/lock-delay=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-delay=${escaped_value}" "${DCONFFILE}"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80776-8
  - DISA-STIG-RHEL-08-020031
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
- name: XCCDF Value var_screensaver_lock_delay # promote to variable
  set_fact:
    var_screensaver_lock_delay: !!str 5
  tags:
    - always

- name: Set GNOME3 Screensaver Lock Delay After Activation Period
  community.general.ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: lock-delay
    value: uint32 {{ var_screensaver_lock_delay }}
    create: true
    no_extra_spaces: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80776-8
  - DISA-STIG-RHEL-08-020031
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80776-8
  - DISA-STIG-RHEL-08-020031
  - NIST-800-171-3.1.10
  - NIST-800-53-AC-11(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_delay
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

screensaver lock is set correctly  oval:ssg-test_screensaver_lock_delay:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_screensaver_lock_delay:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-delay=uint32[\s][0-9]*$1

screensaver lock delay setting is correct  oval:ssg-test_screensaver_lock_delay_setting:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_screensaver_lock_delay_setting:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^lock-delay[\s=]*uint32[\s]([^=\s]*)1
Enable GNOME3 Screensaver Lock After Idle Periodxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled mediumCCE-80777-6

Enable GNOME3 Screensaver Lock After Idle Period

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_screensaver_lock_enabled:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80777-6

References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
pcidssReq-8.1.8
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011
pcidss48.2.8, 8.2
stigidRHEL-08-020030
stigrefSV-230347r1017160_rule
Description
To activate locking of the screensaver in the GNOME3 desktop when it is activated, add or set lock-enabled to true in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/desktop/screensaver]
lock-enabled=true
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.
Rationale
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)lock-enabled(\s*=)/#\1lock-enabled\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*lock-enabled\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution == 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Screensaver Lock After Idle Period
  community.general.ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/screensaver
    option: lock-enabled
    value: 'true'
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution != 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME lock-enabled
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-enabled$
    line: /org/gnome/desktop/screensaver/lock-enabled
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution != 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Screensaver Lock After Idle Period
  community.general.ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/desktop/lockdown
    option: disable-lock-screen
    value: 'false'
    create: true
    no_extra_spaces: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution == 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME disable-lock-screen
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/lockdown/disable-lock-screen$
    line: /org/gnome/desktop/lockdown/disable-lock-screen
    create: true
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution == 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Check GNOME3 screenserver disable-lock-screen false
  ansible.builtin.command: gsettings get org.gnome.desktop.lockdown disable-lock-screen
  register: cmd_out
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution == 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Update GNOME3 screenserver disable-lock-screen false
  ansible.builtin.command: gsettings set org.gnome.desktop.lockdown disable-lock-screen
    false
  when:
  - '"gdm" in ansible_facts.packages'
  - ansible_distribution == 'SLES'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80777-6
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020030
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_screensaver_lock_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

screensaver lock is enabled  oval:ssg-test_screensaver_lock_enabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_screensaver_lock_enabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^\[org/gnome/desktop/screensaver\]([^\n]*\n+)+?lock-enabled=true$1

screensaver lock cannot be changed by user  oval:ssg-test_prevent_user_screensaver_lock:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_screensaver_lock:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks/^.*$^/org/gnome/desktop/screensaver/lock-enabled$1
Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Periodxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_locked mediumCCE-87261-4

Ensure Users Cannot Change GNOME3 Screensaver Lock After Idle Period

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_locked
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_screensaver_lock_locked:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-87261-4

References:
cis-csc1, 12, 15, 16
cjis5.5.5
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
pcidssReq-8.1.8
os-srgSRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011
stigidRHEL-08-020082
stigrefSV-244539r1069325_rule
Description
If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding
/org/gnome/desktop/screensaver/lock-enabled
to /etc/dconf/db/local.d/locks/00-security-settings. For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update.
Rationale
A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not want to logout because of the temporary nature of the absense.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-enabled$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-enabled$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-87261-4
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020082
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME Screensaver lock-enabled
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-enabled$
    line: /org/gnome/desktop/screensaver/lock-enabled
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-87261-4
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020082
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-87261-4
  - CJIS-5.5.5
  - DISA-STIG-RHEL-08-020082
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - dconf_gnome_screensaver_lock_locked
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

screensaver lock cannot be changed by user  oval:ssg-test_prevent_user_screensaver_lock_locked:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_screensaver_lock_locked:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks/^.*$^/org/gnome/desktop/screensaver/lock-enabled$1
Ensure Users Cannot Change GNOME3 Screensaver Settingsxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_user_locks mediumCCE-80780-0

Ensure Users Cannot Change GNOME3 Screensaver Settings

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_user_locks
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_screensaver_user_locks:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80780-0

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.5
stigidRHEL-08-020080
stigrefSV-230354r1069323_rule
Description
If not already configured, ensure that users cannot change GNOME3 screensaver lock settings by adding /org/gnome/desktop/screensaver/lock-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/screensaver/lock-delay
After the settings have been set, run dconf update.
Rationale
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-delay$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/screensaver/lock-delay$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/screensaver/lock-delay$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/screensaver/lock-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80780-0
  - DISA-STIG-RHEL-08-020080
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME lock-delay
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/screensaver/lock-delay$
    line: /org/gnome/desktop/screensaver/lock-delay
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80780-0
  - DISA-STIG-RHEL-08-020080
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80780-0
  - DISA-STIG-RHEL-08-020080
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - dconf_gnome_screensaver_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

screensaver lock delay cannot be changed by user  oval:ssg-test_user_change_lock_delay_lock:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_user_change_lock_delay_lock:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks/^.*$^/org/gnome/desktop/screensaver/lock-delay$1
Ensure Users Cannot Change GNOME3 Session Idle Settingsxccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks mediumCCE-80781-8

Ensure Users Cannot Change GNOME3 Session Idle Settings

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_session_idle_user_locks:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80781-8

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a)
nist-csfPR.AC-7
pcidssReq-8.1.8
os-srgSRG-OS-000029-GPOS-00010, SRG-OS-000031-GPOS-00012
cis1.8.5
pcidss48.2.8, 8.2
stigidRHEL-08-020081
stigrefSV-244538r1069324_rule
Description
If not already configured, ensure that users cannot change GNOME3 session idle settings by adding /org/gnome/desktop/session/idle-delay to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/desktop/session/idle-delay
After the settings have been set, run dconf update.
Rationale
A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity of the information system but does not logout because of the temporary nature of the absence. Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity, GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the session lock. As such, users should not be allowed to change session settings.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/desktop/session/idle-delay$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/desktop/session/idle-delay$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80781-8
  - DISA-STIG-RHEL-08-020081
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME Session idle-delay
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/desktop/session/idle-delay$
    line: /org/gnome/desktop/session/idle-delay
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80781-8
  - DISA-STIG-RHEL-08-020081
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80781-8
  - DISA-STIG-RHEL-08-020081
  - NIST-800-171-3.1.10
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - dconf_gnome_session_idle_user_locks
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

user cannot change screensaver idle delay  oval:ssg-test_user_change_idle_delay_lock:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_user_change_idle_delay_lock:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks/^.*$^/org/gnome/desktop/session/idle-delay$1
Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3xccdf_org.ssgproject.content_rule_dconf_gnome_disable_ctrlaltdel_reboot highCCE-84028-0

Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_disable_ctrlaltdel_reboot
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_disable_ctrlaltdel_reboot:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-84028-0

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.1.2
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1), CM-7(b)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040171
stigrefSV-230530r1069317_rule
Description
By default, GNOME will reboot the system if the Ctrl-Alt-Del key sequence is pressed.

To configure the system to ignore the Ctrl-Alt-Del key sequence from the Graphical User Interface (GUI) instead of rebooting the system, add or set logout to [''] in /etc/dconf/db/local.d/00-security-settings. For example:
[org/gnome/settings-daemon/plugins/media-keys]
logout=['']
Once the settings have been added, add a lock to /etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/settings-daemon/plugins/media-keys/logout
After the settings have been set, run dconf update.
Rationale
A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/settings-daemon/plugins/media-keys\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*logout\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)logout(\s*=)/#\1logout\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/settings-daemon/plugins/media-keys\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/settings-daemon/plugins/media-keys]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "['']")"
if grep -q "^\\s*logout\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*logout\\s*=\\s*.*/logout=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/settings-daemon/plugins/media-keys\\]|a\\logout=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/settings-daemon/plugins/media-keys/logout$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/settings-daemon/plugins/media-keys/logout$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/settings-daemon/plugins/media-keys/logout$" /etc/dconf/db/local.d/
then
    echo "/org/gnome/settings-daemon/plugins/media-keys/logout" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84028-0
  - DISA-STIG-RHEL-08-040171
  - NIST-800-171-3.1.2
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - dconf_gnome_disable_ctrlaltdel_reboot
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Disable Ctrl-Alt-Del Reboot Key Sequence in GNOME3
  community.general.ini_file:
    dest: /etc/dconf/db/local.d/00-security-settings
    section: org/gnome/settings-daemon/plugins/media-keys
    option: logout
    value: '['''']'
    create: true
    no_extra_spaces: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-84028-0
  - DISA-STIG-RHEL-08-040171
  - NIST-800-171-3.1.2
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - dconf_gnome_disable_ctrlaltdel_reboot
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME disablement of Ctrl-Alt-Del
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/local.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/settings-daemon/plugins/media-keys/logout$
    line: /org/gnome/settings-daemon/plugins/media-keys/logout
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-84028-0
  - DISA-STIG-RHEL-08-040171
  - NIST-800-171-3.1.2
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - dconf_gnome_disable_ctrlaltdel_reboot
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-84028-0
  - DISA-STIG-RHEL-08-040171
  - NIST-800-171-3.1.2
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(b)
  - dconf_gnome_disable_ctrlaltdel_reboot
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

Disable Ctrl-Alt-Del  oval:ssg-test_disable_gnome_ctrlaltdel:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_disable_gnome_ctrlaltdel:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/^.*$^\[org/gnome/settings-daemon/plugins/media-keys\]([^\n]*\n+)+?logout[\s]*=[\s]*\[''\]$1

Prevent enabling of ctrl-alt-del keys  oval:ssg-test_prevent_user_enable_ctrlaltdel:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_enable_ctrlaltdel:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/local.d/locks/^.*$^/org/gnome/settings-daemon/plugins/media-keys/logout$1
Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticatexccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate mediumCCE-82202-3

Ensure Users Re-Authenticate for Privilege Escalation - sudo !authenticate

Rule IDxccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sudo_remove_no_authenticate:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82202-3

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.5.1, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-11, CM-6(a)
nist-csfPR.AC-1, PR.AC-7
os-srgSRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158
stigidRHEL-08-010381
stigrefSV-230272r1101898_rule
Description
The sudo !authenticate option, when specified, allows a user to execute commands using sudo without having to authenticate. This should be disabled by making sure that the !authenticate option does not exist in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale
Without re-authentication, users may access resources or perform tasks for which they do not have authorization.

When operating systems provide the capability to escalate a functional capability, it is critical that the user re-authenticate.
OVAL test results details

!authenticate does not exist in /etc/sudoers  oval:ssg-test_no_authenticate_etc_sudoers:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_authenticate_etc_sudoers:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sudoers^(?!#).*[\s]+\!authenticate.*$1

!authenticate does not exist in /etc/sudoers.d  oval:ssg-test_no_authenticate_etc_sudoers_d:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_authenticate_etc_sudoers_d:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/sudoers.d^.*$^(?!#).*[\s]+\!authenticate.*$1
Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWDxccdf_org.ssgproject.content_rule_sudo_remove_nopasswd mediumCCE-82197-5

Ensure Users Re-Authenticate for Privilege Escalation - sudo NOPASSWD

Rule IDxccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sudo_remove_nopasswd:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82197-5

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.5.1, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-11, CM-6(a)
nist-csfPR.AC-1, PR.AC-7
os-srgSRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158
stigidRHEL-08-010380
stigrefSV-230271r1101896_rule
Description
The sudo NOPASSWD tag, when specified, allows a user to execute commands using sudo without having to authenticate. This should be disabled by making sure that the NOPASSWD tag does not exist in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/.
Rationale
Without re-authentication, users may access resources or perform tasks for which they do not have authorization.

When operating systems provide the capability to escalate a functional capability, it is critical that the user re-authenticate.
Warnings
warning  This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. RHV requires to perform operations as root without being asked for password.
OVAL test results details

NOPASSWD does not exist /etc/sudoers  oval:ssg-test_nopasswd_etc_sudoers:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_nopasswd_etc_sudoers:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sudoers^(?!#).*[\s]+NOPASSWD[\s]*\:.*$1

NOPASSWD does not exist in /etc/sudoers.d  oval:ssg-test_nopasswd_etc_sudoers_d:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_nopasswd_etc_sudoers_d:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/sudoers.d^.*$^(?!#).*[\s]+NOPASSWD[\s]*\:.*$1
Require Re-Authentication When Using the sudo Commandxccdf_org.ssgproject.content_rule_sudo_require_reauthentication mediumCCE-87838-9

Require Re-Authentication When Using the sudo Command

Rule IDxccdf_org.ssgproject.content_rule_sudo_require_reauthentication
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sudo_require_reauthentication:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-87838-9

References:
nistIA-11
os-srgSRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158
cis4.3.6
pcidss42.2.6, 2.2
stigidRHEL-08-010384
stigrefSV-237643r1050789_rule
Description
The sudo timestamp_timeout tag sets the amount of time sudo password prompt waits. The default timestamp_timeout value is 5 minutes. The timestamp_timeout should be configured by making sure that the timestamp_timeout tag exists in /etc/sudoers configuration file or any sudo configuration snippets in /etc/sudoers.d/. If the value is set to an integer less than 0, the user's time stamp will not expire and the user will not have to re-authenticate for privileged actions until the user's session is terminated.
Rationale
Without re-authentication, users may access resources or perform tasks for which they do not have authorization.

When operating systems provide the capability to escalate a functional capability, it is critical that the user re-authenticate.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q sudo; }; then

var_sudo_timestamp_timeout='0'


if grep -Px '^[\s]*Defaults.*timestamp_timeout[\s]*=.*' /etc/sudoers.d/*; then
    find /etc/sudoers.d/ -type f -exec sed -Ei "/^[[:blank:]]*Defaults.*timestamp_timeout[[:blank:]]*=.*/d" {} \;
fi

if /usr/sbin/visudo -qcf /etc/sudoers; then
    cp /etc/sudoers /etc/sudoers.bak
    if ! grep -P '^[\s]*Defaults.*timestamp_timeout[\s]*=[\s]*[-]?\w+.*$' /etc/sudoers; then
        # sudoers file doesn't define Option timestamp_timeout
        echo "Defaults timestamp_timeout=${var_sudo_timestamp_timeout}" >> /etc/sudoers
    else
        # sudoers file defines Option timestamp_timeout, remediate wrong values if present
        if grep -qP "^[\s]*Defaults\s.*\btimestamp_timeout[\s]*=[\s]*(?!${var_sudo_timestamp_timeout}\b)[-]?\w+\b.*$" /etc/sudoers; then
            sed -Ei "s/(^[[:blank:]]*Defaults.*timestamp_timeout[[:blank:]]*=)[[:blank:]]*[-]?\w+(.*$)/\1${var_sudo_timestamp_timeout}\2/" /etc/sudoers
        fi
    fi
    
    # Check validity of sudoers and cleanup bak
    if /usr/sbin/visudo -qcf /etc/sudoers; then
        rm -f /etc/sudoers.bak
    else
        echo "Fail to validate remediated /etc/sudoers, reverting to original file."
        mv /etc/sudoers.bak /etc/sudoers
        false
    fi
else
    echo "Skipping remediation, /etc/sudoers failed to validate"
    false
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication
- name: XCCDF Value var_sudo_timestamp_timeout # promote to variable
  set_fact:
    var_sudo_timestamp_timeout: !!str 0
  tags:
    - always

- name: Require Re-Authentication When Using the sudo Command - Find /etc/sudoers.d/*
    files containing 'Defaults timestamp_timeout'
  ansible.builtin.find:
    path: /etc/sudoers.d
    patterns: '*'
    contains: ^[\s]*Defaults\s.*\btimestamp_timeout[\s]*=.*
  register: sudoers_d_defaults_timestamp_timeout
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication

- name: Require Re-Authentication When Using the sudo Command - Remove 'Defaults timestamp_timeout'
    from /etc/sudoers.d/* files
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^[\s]*Defaults\s.*\btimestamp_timeout[\s]*=.*
    state: absent
  with_items: '{{ sudoers_d_defaults_timestamp_timeout.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication

- name: Require Re-Authentication When Using the sudo Command - Ensure timestamp_timeout
    has the appropriate value in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults\s(.*)\btimestamp_timeout[\s]*=[\s]*[-]?\w+\b(.*)$
    line: Defaults \1timestamp_timeout={{ var_sudo_timestamp_timeout }}\2
    validate: /usr/sbin/visudo -cf %s
    backrefs: true
  register: edit_sudoers_timestamp_timeout_option
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication

- name: Require Re-Authentication When Using the sudo Command - Enable timestamp_timeout
    option with correct value in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    line: Defaults timestamp_timeout={{ var_sudo_timestamp_timeout }}
    validate: /usr/sbin/visudo -cf %s
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  - |
    edit_sudoers_timestamp_timeout_option is defined and not edit_sudoers_timestamp_timeout_option.changed
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication

- name: Require Re-Authentication When Using the sudo Command - Remove timestamp_timeout
    wrong values in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: ^[\s]*Defaults\s.*\btimestamp_timeout[\s]*=[\s]*(?!{{ var_sudo_timestamp_timeout
      }}\b)[-]?\w+\b.*$
    state: absent
    validate: /usr/sbin/visudo -cf %s
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-87838-9
  - DISA-STIG-RHEL-08-010384
  - NIST-800-53-IA-11
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudo_require_reauthentication
OVAL test results details

check correct configuration in /etc/sudoers  oval:ssg-test_sudo_timestamp_timeout:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sudo_timestamp_timeout:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\/etc\/(sudoers|sudoers\.d\/.*)$^[\s]*Defaults[\s]+timestamp_timeout[\s]*=\s*[+]?(\d*\.\d+|\d+\.\d*|\d+)$1

check correct configuration in /etc/sudoers  oval:ssg-test_sudo_timestamp_timeout_no_signs:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sudo_timestamp_timeout_no_signs:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\/etc\/(sudoers|sudoers\.d\/.*)$^[\s]*Defaults[\s]+timestamp_timeout[\s]*=\s*[\-](\d*\.\d+|\d+\.\d*|\d+)$1
The operating system must restrict privilege elevation to authorized personnelxccdf_org.ssgproject.content_rule_sudo_restrict_privilege_elevation_to_authorized mediumCCE-83425-9

The operating system must restrict privilege elevation to authorized personnel

Rule IDxccdf_org.ssgproject.content_rule_sudo_restrict_privilege_elevation_to_authorized
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sudo_restrict_privilege_elevation_to_authorized:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-83425-9

References:
nistCM-6(b), CM-6(iv)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010382
stigrefSV-237641r1101904_rule
Description
The sudo command allows a user to execute programs with elevated (administrator) privileges. It prompts the user for their password and confirms your request to execute a command by checking a file, called sudoers. Restrict privileged actions by removing the following entries from the sudoers file: ALL ALL=(ALL) ALL ALL ALL=(ALL:ALL) ALL
Rationale
If the "sudoers" file is not configured correctly, any user defined on the system can initiate privileged actions on the target system.
Warnings
warning  This rule doesn't come with a remediation, as the exact requirement allows exceptions, and removing lines from the sudoers file can make the system non-administrable.
OVAL test results details

Make sure that sudoers has restrictions on which users can run sudo  oval:ssg-test_not_all_users_can_sudo_to_users:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoers_cfg_spec_all_users:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^\s*ALL\s+ALL\=\(ALL\)\s+ALL\s*$1

Make sure that sudoers has restrictions on which users can run sudo  oval:ssg-test_not_all_users_can_sudo_to_group:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoers_cfg_spec_all_group:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^\s*ALL\s+ALL\=\(ALL\:ALL\)\s+ALL\s*1
Ensure sudo only includes the default configuration directoryxccdf_org.ssgproject.content_rule_sudoers_default_includedir mediumCCE-86377-9

Ensure sudo only includes the default configuration directory

Rule IDxccdf_org.ssgproject.content_rule_sudoers_default_includedir
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sudoers_default_includedir:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86377-9

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010379
stigrefSV-251711r1017365_rule
Description
Administrators can configure authorized sudo users via drop-in files, and it is possible to include other directories and configuration files from the file currently being parsed. Make sure that /etc/sudoers only includes drop-in configuration files from /etc/sudoers.d, or that no drop-in file is included. Either the /etc/sudoers should contain only one #includedir directive pointing to /etc/sudoers.d, and no file in /etc/sudoers.d/ should include other files or directories; Or the /etc/sudoers should not contain any #include, @include, #includedir or @includedir directives. Note that the '#' character doesn't denote a comment in the configuration file.
Rationale
Some sudo configurtion options allow users to run programs without re-authenticating. Use of these configuration options makes it easier for one compromised accound to be used to compromise other accounts.
OVAL test results details

test none sudoers #include or @include  oval:ssg-test_sudoers_without_include:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoers_without_include:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sudoers^[#@]include[\s]+.*$1

test none sudoers #includedir or @includdir  oval:ssg-test_sudoers_without_includedir:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/sudoers#includedir /etc/sudoers.d

test only one sudoers #includedir  oval:ssg-test_sudoers_default_includedir:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/sudoers#includedir /etc/sudoers.d

test none sudoers #include or @include  oval:ssg-test_sudoers_without_include:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoers_without_include:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sudoers^[#@]include[\s]+.*$1

test none sudoers @includedir  oval:ssg-test_sudoers_without_includedir_new:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoers_without_include_new:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sudoers^@includedir[\s]+.*$1

test none sudoers.d #include, @include, #includedir or @includedir  oval:ssg-test_sudoersd_without_includes:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sudoersd_without_includes:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/sudoers.d/.*^[#@]include(?:dir)?[\s]+.*$1
Ensure invoking users password for privilege escalation when using sudoxccdf_org.ssgproject.content_rule_sudoers_validate_passwd mediumCCE-83422-6

Ensure invoking users password for privilege escalation when using sudo

Rule IDxccdf_org.ssgproject.content_rule_sudoers_validate_passwd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sudoers_validate_passwd:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-83422-6

References:
nistCM-6(b), CM-6.1(iv)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010383
stigrefSV-237642r991589_rule
Description
The sudoers security policy requires that users authenticate themselves before they can use sudo. When sudoers requires authentication, it validates the invoking user's credentials. The expected output for:
 sudo cvtsudoers -f sudoers /etc/sudoers | grep -E '^Defaults !?(rootpw|targetpw|runaspw)$' 
 Defaults !targetpw
      Defaults !rootpw
      Defaults !runaspw 
or if cvtsudoers not supported:
 sudo find /etc/sudoers /etc/sudoers.d \( \! -name '*~' -a \! -name '*.*' \) -exec grep -E --with-filename '^[[:blank:]]*Defaults[[:blank:]](.*[[:blank:]])?!?\b(rootpw|targetpw|runaspw)' -- {} \; 
 /etc/sudoers:Defaults !targetpw
      /etc/sudoers:Defaults !rootpw
      /etc/sudoers:Defaults !runaspw 
Rationale
If the rootpw, targetpw, or runaspw flags are defined and not disabled, by default the operating system will prompt the invoking user for the "root" user password.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q sudo; }; then

if grep -x '^Defaults targetpw$' /etc/sudoers; then
    sed -i "/Defaults targetpw/d" /etc/sudoers \;
fi
if grep -x '^Defaults targetpw$' /etc/sudoers.d/*; then
    find /etc/sudoers.d/ -type f -exec sed -i "/Defaults targetpw/d" {} \;
fi
if grep -x '^Defaults rootpw$' /etc/sudoers; then
    sed -i "/Defaults rootpw/d" /etc/sudoers \;
fi
if grep -x '^Defaults rootpw$' /etc/sudoers.d/*; then
    find /etc/sudoers.d/ -type f -exec sed -i "/Defaults rootpw/d" {} \;
fi
if grep -x '^Defaults runaspw$' /etc/sudoers; then
    sed -i "/Defaults runaspw/d" /etc/sudoers \;
fi
if grep -x '^Defaults runaspw$' /etc/sudoers.d/*; then
    find /etc/sudoers.d/ -type f -exec sed -i "/Defaults runaspw/d" {} \;
fi

if [ -e "/etc/sudoers" ] ; then
    
    LC_ALL=C sed -i "/Defaults !targetpw/d" "/etc/sudoers"
else
    touch "/etc/sudoers"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/sudoers"

cp "/etc/sudoers" "/etc/sudoers.bak"
# Insert at the end of the file
printf '%s\n' "Defaults !targetpw" >> "/etc/sudoers"
# Clean up after ourselves.
rm "/etc/sudoers.bak"
if [ -e "/etc/sudoers" ] ; then
    
    LC_ALL=C sed -i "/Defaults !rootpw/d" "/etc/sudoers"
else
    touch "/etc/sudoers"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/sudoers"

cp "/etc/sudoers" "/etc/sudoers.bak"
# Insert at the end of the file
printf '%s\n' "Defaults !rootpw" >> "/etc/sudoers"
# Clean up after ourselves.
rm "/etc/sudoers.bak"
if [ -e "/etc/sudoers" ] ; then
    
    LC_ALL=C sed -i "/Defaults !runaspw/d" "/etc/sudoers"
else
    touch "/etc/sudoers"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/sudoers"

cp "/etc/sudoers" "/etc/sudoers.bak"
# Insert at the end of the file
printf '%s\n' "Defaults !runaspw" >> "/etc/sudoers"
# Clean up after ourselves.
rm "/etc/sudoers.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Find out if /etc/sudoers.d/* files contain Defaults targetpw to be deduplicated
  ansible.builtin.find:
    path: /etc/sudoers.d
    patterns: '*'
    contains: ^Defaults targetpw$
  register: sudoers_d_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove found occurrences of Defaults targetpw from /etc/sudoers.d/* files
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^Defaults targetpw$
    state: absent
  with_items: '{{ sudoers_d_defaults.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Find out if /etc/sudoers.d/* files contain Defaults rootpw to be deduplicated
  ansible.builtin.find:
    path: /etc/sudoers.d
    patterns: '*'
    contains: ^Defaults rootpw$
  register: sudoers_d_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove found occurrences of Defaults rootpw from /etc/sudoers.d/* files
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^Defaults rootpw$
    state: absent
  with_items: '{{ sudoers_d_defaults.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Find out if /etc/sudoers.d/* files contain Defaults runaspw to be deduplicated
  ansible.builtin.find:
    path: /etc/sudoers.d
    patterns: '*'
    contains: ^Defaults runaspw$
  register: sudoers_d_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove found occurrences of Defaults runaspw from /etc/sudoers.d/* files
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^Defaults runaspw$
    state: absent
  with_items: '{{ sudoers_d_defaults.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove any ocurrences of Defaults targetpw in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: ^Defaults targetpw$
    validate: /usr/sbin/visudo -cf %s
    state: absent
  register: sudoers_file_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove any ocurrences of Defaults rootpw in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: ^Defaults rootpw$
    validate: /usr/sbin/visudo -cf %s
    state: absent
  register: sudoers_file_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Remove any ocurrences of Defaults runaspw in /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    regexp: ^Defaults runaspw$
    validate: /usr/sbin/visudo -cf %s
    state: absent
  register: sudoers_file_defaults
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Check for duplicate values
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !targetpw$
    state: absent
  check_mode: true
  changed_when: false
  register: dupes
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Deduplicate values from /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !targetpw$
    state: absent
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  - dupes.found is defined and dupes.found > 1
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Insert correct line into /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !targetpw$
    line: Defaults !targetpw
    state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Check for duplicate values
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !rootpw$
    state: absent
  check_mode: true
  changed_when: false
  register: dupes
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Deduplicate values from /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !rootpw$
    state: absent
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  - dupes.found is defined and dupes.found > 1
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Insert correct line into /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !rootpw$
    line: Defaults !rootpw
    state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Check for duplicate values
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !runaspw$
    state: absent
  check_mode: true
  changed_when: false
  register: dupes
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Deduplicate values from /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !runaspw$
    state: absent
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  - dupes.found is defined and dupes.found > 1
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd

- name: Insert correct line into /etc/sudoers
  ansible.builtin.lineinfile:
    path: /etc/sudoers
    create: false
    regexp: ^Defaults !runaspw$
    line: Defaults !runaspw
    state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"sudo" in ansible_facts.packages'
  tags:
  - CCE-83422-6
  - DISA-STIG-RHEL-08-010383
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sudoers_validate_passwd
OVAL test results details

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_targetpw_config:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_targetpw_config:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults !targetpw$\r?\n1

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_rootpw_config:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_rootpw_config:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults !rootpw$\r?\n1

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_runaspw_config:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_runaspw_config:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults !runaspw$\r?\n1

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_targetpw_not_defined:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_targetpw_not_defined:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults targetpw$\r?\n1

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_rootpw_not_defined:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_rootpw_not_defined:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults rootpw$\r?\n1

Ensure invoking user's password for privilege escalation when using sudo  oval:ssg-test_sudoers_runaspw_not_defined:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_test_sudoers_runaspw_not_defined:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^Defaults runaspw$\r?\n1
Install rng-tools Packagexccdf_org.ssgproject.content_rule_package_rng-tools_installed lowCCE-82968-9

Install rng-tools Package

Rule IDxccdf_org.ssgproject.content_rule_package_rng-tools_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_rng-tools_installed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82968-9

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010472
stigrefSV-244527r1017333_rule
Description
The rng-tools package can be installed with the following command:
$ sudo yum install rng-tools
Rationale
rng-tools provides hardware random number generator tools, such as those used in the formation of x509/PKI certificates.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if ( ! ( [ "$(sysctl -a | grep -c 'fips_enabled.*1')" -eq 1 ] ) && rpm --quiet -q kernel ); then

if ! rpm -q --quiet "rng-tools" ; then
    yum install -y "rng-tools"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82968-9
  - DISA-STIG-RHEL-08-010472
  - enable_strategy
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - package_rng-tools_installed

- name: Ensure rng-tools is installed
  ansible.builtin.package:
    name: rng-tools
    state: present
  when: ( "kernel" in ansible_facts.packages )
  tags:
  - CCE-82968-9
  - DISA-STIG-RHEL-08-010472
  - enable_strategy
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - package_rng-tools_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_rng-tools

class install_rng-tools {
  package { 'rng-tools':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=rng-tools


[[packages]]
name = "rng-tools"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install rng-tools

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install rng-tools
OVAL test results details

package rng-tools is installed  oval:ssg-test_package_rng-tools_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_rng-tools_installed:obj:1 of type rpminfo_object
Name
rng-tools
Uninstall abrt-addon-ccpp Packagexccdf_org.ssgproject.content_rule_package_abrt-addon-ccpp_removed lowCCE-82919-2

Uninstall abrt-addon-ccpp Package

Rule IDxccdf_org.ssgproject.content_rule_package_abrt-addon-ccpp_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_abrt-addon-ccpp_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82919-2

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The abrt-addon-ccpp package can be removed with the following command:
$ sudo yum erase abrt-addon-ccpp
Rationale
abrt-addon-ccpp contains hooks for C/C++ crashed programs and abrt's C/C++ analyzer plugin.
OVAL test results details

package abrt-addon-ccpp is removed  oval:ssg-test_package_abrt-addon-ccpp_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_abrt-addon-ccpp_removed:obj:1 of type rpminfo_object
Name
abrt-addon-ccpp
Uninstall abrt-addon-kerneloops Packagexccdf_org.ssgproject.content_rule_package_abrt-addon-kerneloops_removed lowCCE-82926-7

Uninstall abrt-addon-kerneloops Package

Rule IDxccdf_org.ssgproject.content_rule_package_abrt-addon-kerneloops_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_abrt-addon-kerneloops_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82926-7

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The abrt-addon-kerneloops package can be removed with the following command:
$ sudo yum erase abrt-addon-kerneloops
Rationale
abrt-addon-kerneloops contains plugins for collecting kernel crash information and reporter plugin which sends this information to a specified server, usually to kerneloops.org.
OVAL test results details

package abrt-addon-kerneloops is removed  oval:ssg-test_package_abrt-addon-kerneloops_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_abrt-addon-kerneloops_removed:obj:1 of type rpminfo_object
Name
abrt-addon-kerneloops
Uninstall abrt-cli Packagexccdf_org.ssgproject.content_rule_package_abrt-cli_removed lowCCE-82907-7

Uninstall abrt-cli Package

Rule IDxccdf_org.ssgproject.content_rule_package_abrt-cli_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_abrt-cli_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82907-7

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The abrt-cli package can be removed with the following command:
$ sudo yum erase abrt-cli
Rationale
abrt-cli contains a command line client for controlling abrt daemon over sockets.
OVAL test results details

package abrt-cli is removed  oval:ssg-test_package_abrt-cli_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_abrt-cli_removed:obj:1 of type rpminfo_object
Name
abrt-cli
Uninstall abrt-plugin-sosreport Packagexccdf_org.ssgproject.content_rule_package_abrt-plugin-sosreport_removed lowCCE-82910-1

Uninstall abrt-plugin-sosreport Package

Rule IDxccdf_org.ssgproject.content_rule_package_abrt-plugin-sosreport_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_abrt-plugin-sosreport_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82910-1

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The abrt-plugin-sosreport package can be removed with the following command:
$ sudo yum erase abrt-plugin-sosreport
Rationale
abrt-plugin-sosreport provides a plugin to include an sosreport in an ABRT report.
OVAL test results details

package abrt-plugin-sosreport is removed  oval:ssg-test_package_abrt-plugin-sosreport_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_abrt-plugin-sosreport_removed:obj:1 of type rpminfo_object
Name
abrt-plugin-sosreport
Uninstall gssproxy Packagexccdf_org.ssgproject.content_rule_package_gssproxy_removed mediumCCE-82943-2

Uninstall gssproxy Package

Rule IDxccdf_org.ssgproject.content_rule_package_gssproxy_removed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_gssproxy_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82943-2

References:
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040370
stigrefSV-230559r1014820_rule
Description
The gssproxy package can be removed with the following command:
$ sudo yum erase gssproxy
Rationale
gssproxy is a proxy for GSS API credential handling. Kerberos relies on some key derivation functions that may not be compatible with some site policies such as FIPS 140.
Warnings
warning  This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. RHV uses NFS storage, which has dependency on gssproxy.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

# CAUTION: This remediation script will remove gssproxy
# from the system, and may remove any packages
# that depend on gssproxy. Execute this
# remediation AFTER testing on a non-production
# system!


if rpm -q --quiet "gssproxy" ; then
yum remove -y "gssproxy"
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: 'Uninstall gssproxy Package: Ensure gssproxy is removed'
  ansible.builtin.package:
    name: gssproxy
    state: absent
  tags:
  - CCE-82943-2
  - DISA-STIG-RHEL-08-040370
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_gssproxy_removed

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

include remove_gssproxy

class remove_gssproxy {
  package { 'gssproxy':
    ensure => 'purged',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package remove gssproxy

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


dnf remove gssproxy
OVAL test results details

package gssproxy is removed  oval:ssg-test_package_gssproxy_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedgssproxyx86_64(none)21.el80.8.00:0.8.0-21.el8199e2f91fd431d51gssproxy-0:0.8.0-21.el8.x86_64
Uninstall iprutils Packagexccdf_org.ssgproject.content_rule_package_iprutils_removed mediumCCE-82946-5

Uninstall iprutils Package

Rule IDxccdf_org.ssgproject.content_rule_package_iprutils_removed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_iprutils_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82946-5

References:
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040380
stigrefSV-230560r1017321_rule
Description
The iprutils package can be removed with the following command:
$ sudo yum erase iprutils
Rationale
iprutils provides a suite of utlilities to manage and configure SCSI devices supported by the ipr SCSI storage device driver.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

# CAUTION: This remediation script will remove iprutils
# from the system, and may remove any packages
# that depend on iprutils. Execute this
# remediation AFTER testing on a non-production
# system!


if rpm -q --quiet "iprutils" ; then
yum remove -y "iprutils"
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: 'Uninstall iprutils Package: Ensure iprutils is removed'
  ansible.builtin.package:
    name: iprutils
    state: absent
  tags:
  - CCE-82946-5
  - DISA-STIG-RHEL-08-040380
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_iprutils_removed

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

include remove_iprutils

class remove_iprutils {
  package { 'iprutils':
    ensure => 'purged',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package --remove=iprutils

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package remove iprutils

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


dnf remove iprutils
OVAL test results details

package iprutils is removed  oval:ssg-test_package_iprutils_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatediprutilsx86_64(none)1.el82.4.190:2.4.19-1.el8199e2f91fd431d51iprutils-0:2.4.19-1.el8.x86_64
Uninstall krb5-workstation Packagexccdf_org.ssgproject.content_rule_package_krb5-workstation_removed mediumCCE-82931-7

Uninstall krb5-workstation Package

Rule IDxccdf_org.ssgproject.content_rule_package_krb5-workstation_removed
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82931-7

References:
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000120-GPOS-00061
stigidRHEL-08-010162
stigrefSV-230239r1017058_rule
Description
The krb5-workstation package can be removed with the following command:
$ sudo yum erase krb5-workstation
Rationale
Kerberos is a network authentication system. The krb5-workstation package contains the basic Kerberos programs (kinit, klist, kdestroy, kpasswd).
Warnings
warning  This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. RHV hosts require ipa-client package, which has dependency on krb5-workstation.
Uninstall libreport-plugin-logger Packagexccdf_org.ssgproject.content_rule_package_libreport-plugin-logger_removed lowCCE-89201-8

Uninstall libreport-plugin-logger Package

Rule IDxccdf_org.ssgproject.content_rule_package_libreport-plugin-logger_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_libreport-plugin-logger_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-89201-8

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The libreport-plugin-logger package can be removed with the following command:
$ sudo yum erase libreport-plugin-logger
Rationale
libreport-plugin-logger is a ABRT plugin to report bugs into the Red Hat Support system.
OVAL test results details

package libreport-plugin-logger is removed  oval:ssg-test_package_libreport-plugin-logger_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_libreport-plugin-logger_removed:obj:1 of type rpminfo_object
Name
libreport-plugin-logger
Uninstall libreport-plugin-rhtsupport Packagexccdf_org.ssgproject.content_rule_package_libreport-plugin-rhtsupport_removed lowCCE-88955-0

Uninstall libreport-plugin-rhtsupport Package

Rule IDxccdf_org.ssgproject.content_rule_package_libreport-plugin-rhtsupport_removed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_libreport-plugin-rhtsupport_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-88955-0

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The libreport-plugin-rhtsupport package can be removed with the following command:
$ sudo yum erase libreport-plugin-rhtsupport
Rationale
libreport-plugin-rhtsupport is a ABRT plugin to report bugs into the Red Hat Support system.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

# CAUTION: This remediation script will remove libreport-plugin-rhtsupport
# from the system, and may remove any packages
# that depend on libreport-plugin-rhtsupport. Execute this
# remediation AFTER testing on a non-production
# system!


if rpm -q --quiet "libreport-plugin-rhtsupport" ; then
yum remove -y "libreport-plugin-rhtsupport"
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: 'Uninstall libreport-plugin-rhtsupport Package: Ensure libreport-plugin-rhtsupport
    is removed'
  ansible.builtin.package:
    name: libreport-plugin-rhtsupport
    state: absent
  tags:
  - CCE-88955-0
  - DISA-STIG-RHEL-08-040001
  - disable_strategy
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - package_libreport-plugin-rhtsupport_removed

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

include remove_libreport-plugin-rhtsupport

class remove_libreport-plugin-rhtsupport {
  package { 'libreport-plugin-rhtsupport':
    ensure => 'purged',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package --remove=libreport-plugin-rhtsupport

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package remove libreport-plugin-rhtsupport

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


dnf remove libreport-plugin-rhtsupport
OVAL test results details

package libreport-plugin-rhtsupport is removed  oval:ssg-test_package_libreport-plugin-rhtsupport_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedlibreport-plugin-rhtsupportx86_64(none)15.el82.9.50:2.9.5-15.el8199e2f91fd431d51libreport-plugin-rhtsupport-0:2.9.5-15.el8.x86_64
Uninstall python3-abrt-addon Packagexccdf_org.ssgproject.content_rule_package_python3-abrt-addon_removed lowCCE-86084-1

Uninstall python3-abrt-addon Package

Rule IDxccdf_org.ssgproject.content_rule_package_python3-abrt-addon_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_python3-abrt-addon_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-86084-1

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The python3-abrt-addon package can be removed with the following command:
$ sudo yum erase python3-abrt-addon
Rationale
python3-abrt-addon contains python hook and python analyzer plugin for handling uncaught exceptions in python programs.
OVAL test results details

package python3-abrt-addon is removed  oval:ssg-test_package_python3-abrt-addon_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_python3-abrt-addon_removed:obj:1 of type rpminfo_object
Name
python3-abrt-addon
Uninstall tuned Packagexccdf_org.ssgproject.content_rule_package_tuned_removed mediumCCE-82904-4

Uninstall tuned Package

Rule IDxccdf_org.ssgproject.content_rule_package_tuned_removed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_tuned_removed:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82904-4

References:
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040390
stigrefSV-230561r1017322_rule
Description
The tuned package can be removed with the following command:
$ sudo yum erase tuned
Rationale
tuned contains a daemon that tunes the system settings dynamically. It does so by monitoring the usage of several system components periodically. Based on that information, components will then be put into lower or higher power savings modes to adapt to the current usage.
Warnings
warning  This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. RHV requires tuned package for tuning profiles that can enhance virtualization performance.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

# CAUTION: This remediation script will remove tuned
# from the system, and may remove any packages
# that depend on tuned. Execute this
# remediation AFTER testing on a non-production
# system!


if rpm -q --quiet "tuned" ; then
yum remove -y "tuned"
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: 'Uninstall tuned Package: Ensure tuned is removed'
  ansible.builtin.package:
    name: tuned
    state: absent
  tags:
  - CCE-82904-4
  - DISA-STIG-RHEL-08-040390
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_tuned_removed

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

include remove_tuned

class remove_tuned {
  package { 'tuned':
    ensure => 'purged',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package --remove=tuned

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


package remove tuned

Complexity:low
Disruption:low
Reboot:false
Strategy:disable


dnf remove tuned
OVAL test results details

package tuned is removed  oval:ssg-test_package_tuned_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedtunednoarch(none)4.el8_102.22.10:2.22.1-4.el8_10199e2f91fd431d51tuned-0:2.22.1-4.el8_10.noarch
Ensure yum Removes Previous Package Versionsxccdf_org.ssgproject.content_rule_clean_components_post_updating lowCCE-82476-3

Ensure yum Removes Previous Package Versions

Rule IDxccdf_org.ssgproject.content_rule_clean_components_post_updating
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-clean_components_post_updating:def:1
Time2025-11-18T17:05:00-05:00
Severitylow
Identifiers:

CCE-82476-3

References:
cis-csc18, 20, 4
cobit5APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02
cui3.4.8
isa-62443-20094.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9
iso27001-2013A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3
nistSI-2(6), CM-11(a), CM-11(b), CM-6(a)
nist-csfID.RA-1, PR.IP-12
os-srgSRG-OS-000437-GPOS-00194
stigidRHEL-08-010440
stigrefSV-230281r958936_rule
Description
yum should be configured to remove previous software components after new versions have been installed. To configure yum to remove the previous software components after updating, set the clean_requirements_on_remove to 1 in /etc/yum.conf.
Rationale
Previous versions of software components that are not removed from the information system after updates have been installed may be exploited by some adversaries.
OVAL test results details

check value of clean_requirements_on_remove in /etc/yum.conf  oval:ssg-test_yum_clean_components_post_updating:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/yum.confclean_requirements_on_remove=True
Ensure gpgcheck Is Enabled for All Package Repositoriesxccdf_org.ssgproject.content_rule_enable_gpgcheck_for_all_repositories highCCE-86187-2

Ensure gpgcheck Is Enabled for All Package Repositories

Rule IDxccdf_org.ssgproject.content_rule_enable_gpgcheck_for_all_repositories
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-enable_gpgcheck_for_all_repositories:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-86187-2

References:
os-srgSRG-OS-000366-GPOS-00153
stigidRHEL-08-010370
stigrefSV-230264r1017377_rule
Description
To ensure signature checking is enabled for all package repositories, the gpgcheck option must be enabled for all repos. Configure the operating system to verify the signature of packages from a repository prior to install by setting the following option in the "/etc/yum.repos.d/[your_repo_name].repo" file:
gpgcheck=1
Rationale
Changes to any software components can have significant effects on the overall security of the operating system. This requirement ensures the software has not been tampered with and that it has been provided by a trusted vendor.


function replace_all_gpgcheck {
    sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/*
}

function add_gpgcheck_where_missing {
    for repofile in /etc/yum.repos.d/* ; do
        declare -a sections_without_gpgcheck
        section="false"
        section_has_gpgcheck="false"
        section_name=""
        while IFS= read -r line; do
            if grep -qP '^\[.*\]$' <( echo "$line" ) ; then
                # new section starts
                if [[ "$section" == "true" ]] && [[ "$section_has_gpgcheck" == "false" ]] ; then
                    sections_without_gpgcheck+=("$section_name")
                    section="false"
                fi
                section="true"
                section_has_gpgcheck="false"
                section_name=$(echo "$line" | sed -n 's/^\[\(.*\)\]$/\1/p')
            fi
            if grep -qP '^\s*gpgcheck\s*=\s*' <( echo "$line" ) ; then
                section_has_gpgcheck="true"
            fi
        done < "$repofile"
        if [[ "$section" == "true" ]] && [[ "$section_has_gpgcheck" == "false" ]] ; then
            sections_without_gpgcheck+=("$section_name")
            section="false"
        fi

        for x in "${sections_without_gpgcheck[@]}" ; do
            sed -i "s/^\[$x\]/&\ngpgcheck=1/" "$repofile"
        done
    done
}

replace_all_gpgcheck
add_gpgcheck_where_missing

Complexity:low
Disruption:medium
Reboot:false
Strategy:enable
- name: 'Ensure gpgcheck Is Enabled for All Package Repositories: Grep for yum repo
    section names'
  ansible.builtin.shell: |
    set -o pipefail
    grep -HEr '^\[.+\]' -r /etc/yum.repos.d/
  register: repo_grep_results
  failed_when: repo_grep_results.rc not in [0, 1]
  changed_when: false
  tags:
  - CCE-86187-2
  - DISA-STIG-RHEL-08-010370
  - enable_gpgcheck_for_all_repositories
  - enable_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed

- name: 'Ensure gpgcheck Is Enabled for All Package Repositories: Set gpgcheck=1 for
    each yum repo'
  community.general.ini_file:
    path: '{{ item[0] }}'
    section: '{{ item[1] }}'
    option: gpgcheck
    value: '1'
    no_extra_spaces: true
  loop: '{{ repo_grep_results.stdout | regex_findall( ''(.+\.repo):\[(.+)\]\n?'' )
    if repo_grep_results is not skipped else [] }}'
  when: repo_grep_results is not skipped
  tags:
  - CCE-86187-2
  - DISA-STIG-RHEL-08-010370
  - enable_gpgcheck_for_all_repositories
  - enable_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
OVAL test results details

verify all repos in /etc/yum.repos.d have gpgcheck enabled  oval:ssg-test_enable_gpgcheck_for_all_repositories_all_enabled:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/yum.repos.d/redhat.repo [network-observability-0.1.3-for-rhel-8-x86_64-files] name = Network Observability (Files) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/NETOBSERV/1.0/files enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[openshift-serverless-1-for-rhel-8-x86_64-source-rpms] name = Red Hat Openshift Serverless 1 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhoss/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[lvms-4.14-for-rhel-8-x86_64-debug-rpms] name = Logical Volume Manager Storage 4.14 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/lvms/4.14/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.13-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.13 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.16-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.16 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.16/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.14-for-rhel-8-x86_64-debug-rpms] name = OpenShift Developer Tools and Services 4.14 (RHEL 8) (x86_64 Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.14/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[openshift-builds-1.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Builds 1.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openshift-builds/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhosds-textonly-3-for-middleware-rpms] name = Red Hat OpenShift Dev Spaces 3 Container Advisories baseurl = https://cdn.redhat.com/content/dist/middleware/rhosds/3.0/x86_64/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.18-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.18 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.18/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.6-for-rhel-8-x86_64-source-rpms] name = OpenShift Developer Tools and Services 4.6 (RHEL 8) (x86_64 Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.7/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.12-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.12 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.12/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.15-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 4.15 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.15/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/local-appstream.repo[local-AppStream] name=Local AppStream baseurl=file:///opt/localrepo/AppStream enabled=1 gpgcheck=0
true/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.4-for-rhel-8-x86_64-rpms] name = Red Hat Ansible Automation Platform 2.4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[quarkus-1-for-rhel-8-x86_64-rpms] name = Red Hat build of Quarkus 1 (RHEL 8) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/quarkus/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.9-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.9 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-2.5-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 2.5 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.5/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.1-for-rhel-8-x86_64-debug-rpms] name = OpenShift Pipelines 1.1 (RHEL 8) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[discovery-1-for-rhel-8-x86_64-rpms] name = Red Hat Discovery 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/discovery/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[amq-clients-3-for-rhel-8-x86_64-source-rpms] name = Red Hat AMQ Clients 3 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/amq/3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.15-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.15 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.13-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift GitOps 1.13 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.13/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.9-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.9 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.9/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift GitOps 1.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.8-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 4.8 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.8/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.12-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.12 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.12/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[automation-hub-4.2-for-rhel-8-x86_64-source-rpms] name = Red Hat Automation Hub 4.2 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/automation-hub/4.2/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-eus-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.7/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[openstack-15-tools-for-rhel-8-x86_64-rpms] name = Red Hat OpenStack Platform 15 Tools for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openstack-tools/15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.5-for-rhel-8-x86_64-rpms] name = OpenShift Tools and Services 4.5 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.5/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.15-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.15 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.3-for-rhel-8-x86_64-source-rpms] name = OpenShift Tools and Services 4.3 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhods-1.20-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Data Science 1.20 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhods/1.20/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[application-interconnect-1-for-rhel-8-x86_64-source-rpms] name = Red Hat Application Interconnect for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhai/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[interconnect-2-for-rhel-8-x86_64-debug-rpms] name = JBoss Interconnect 2 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/interconnect/2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.3-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Pipelines 1.3 (for RHEL 8 x86_64) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.3/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.9-for-rhel-8-x86_64-eus-debug-rpms] name = Red Hat Satellite Tools 6.9 for RHEL 8 x86_64 - Extended Update Support (Debug RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-6-client-2-for-rhel-8-x86_64-rpms] name = Red Hat Satellite 6 Client 2 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-client-2/6/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.15-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.15 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.15/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.6-for-rhel-8-x86_64-e4s-source-rpms] name = Red Hat Satellite Tools 6.6 for RHEL 8 x86_64 - Update Services SAP Solutions (Source RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.16-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift GitOps 1.16 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.16/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-e4s-source-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 - Update Services SAP Solutions (Source RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.7/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.13-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.13 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.12-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.12 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.12/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.1-for-rhel-8-x86_64-source-rpms] name = OpenShift Pipelines 1.1 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhel-8-for-x86_64-baseos-eus-source-rpms] name = Red Hat Enterprise Linux 8 for x86_64 - BaseOS - Extended Update Support (Source RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/baseos/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.12-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 4.12 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.12/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rodoo-1-for-rhel-8-x86_64-rpms] name = Run Once Duration Override Operator (RODOO) 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rodoo/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.19-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.19 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.19/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhoss-1.29-for-rhel-8-x86_64-source-rpms] name = Red Hat Openshift Serverless 1.29 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhoss/1.29/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.9-for-rhel-8-x86_64-debug-rpms] name = OpenShift Developer Tools and Services 4.9 (RHEL 8) (x86_64 Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhosdt-2-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift distributed tracing 2 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosdt/2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhel-atomic-7-cdk-3.3-rpms] name = Red Hat Container Development Kit 3.3 /(RPMs) baseurl = https://cdn.redhat.com/content/dist/rhel/atomic/7/7Server/$basearch/cdk/3.3/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.13-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 4.13 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.6-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.6 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rh-gluster-3-client-for-rhel-8-x86_64-debug-rpms] name = Red Hat Storage Native Client for RHEL 8 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhgs-client/3.5/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.9-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6.9 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.9/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-2.2-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 2.2 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.2/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhosj-1.24-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Jaeger 1.24 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosj/1.24/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.13-for-rhel-8-x86_64-source-rpms] name = OpenShift Developer Tools and Services 4.13 (RHEL 8) (x86_64 Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat Ansible Automation Platform 2.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.4-for-rhel-8-x86_64-rpms] name = OpenShift Tools and Services 4.4 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-utils-6.15-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Utils 6.15 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-utils/6.15/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[jb-coreservices-1-for-rhel-8-x86_64-rhui-debug-rpms] name = JBoss Core Services (RHEL 8) (Debug RPMs) from RHUI baseurl = https://cdn.redhat.com/content/dist/layered/rhui/rhel8/x86_64/jbcs/1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.8-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6.8 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.8/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.18-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 4.18 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.18/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-utils-6.11-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Utils 6.11 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-utils/6.11/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.7-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Pipelines 1.7 (for RHEL 8 x86_64) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.7/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[discovery-1-for-rhel-8-x86_64-debug-rpms] name = Red Hat Discovery 1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/discovery/1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[jb-coreservices-1-for-rhel-8-x86_64-rpms] name = JBoss Core Services (RHEL 8) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/jbcs/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.5-for-rhel-8-x86_64-e4s-rpms] name = Red Hat Satellite Tools 6.5 for RHEL 8 x86_64 - Update Services SAP Solutions (RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.5/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhmtc-for-rhel-8-x86_64-rpms] name = Red Hat Migration Toolkit for Containers for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhmtc/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.11-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.11 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.11/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhosj-1.20-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Jaeger 1.20.0 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosj/1.20/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.4-for-rhel-8-x86_64-source-rpms] name = Red Hat Ansible Automation Platform 2.4 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.4/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[gitops-1.8-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift GitOps 1.8 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.8/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6.10-for-rhel-8-x86_64-eus-rpms] name = Red Hat Satellite Tools 6.10 for RHEL 8 x86_64 - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.10/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.13-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.13 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.13/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhwa-snr-1-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Workload Availability - Self Node Remediation 1 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-snr/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-4.10-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 4.10 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.10/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ocp-tools-4.8-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.8 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.8/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[openstack-15-tools-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenStack Platform 15 Tools for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openstack-tools/15/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6-beta-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6 Beta for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/sat-tools/6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-tools-6-beta-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Tools 6 Beta for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/sat-tools/6/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhel-8-for-x86_64-baseos-eus-rpms] name = Red Hat Enterprise Linux 8 for x86_64 - BaseOS - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/baseos/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[jb-eap-7.4-for-rhel-8-x86_64-rhui-debug-rpms] name = JBoss Enterprise Application Platform 7.4 (RHEL 8) (Debug RPMs) from RHUI baseurl = https://cdn.redhat.com/content/dist/layered/rhui/rhel8/x86_64/jbeap/7.4/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-6-client-2-for-rhel-8-x86_64-e4s-debug-rpms] name = Red Hat Satellite 6 Client 2 for RHEL 8 x86_64 - Update Services SAP Solutions (Debug RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-client-2/6/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhv-4-tools-beta-for-rhel-8-x86_64-debug-rpms] name = Red Hat Virtualization 4 Tools Beta for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/rhv-tools/4/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.16-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.16 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.16/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[cnv-2.4-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 2.4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhocp-4.10-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.10 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.10/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhceph-4-tools-for-rhel-8-x86_64-rpms] name = Red Hat Ceph Storage Tools 4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhceph-tools/4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[satellite-client-6-for-rhel-8-x86_64-e4s-rpms] name = Red Hat Satellite Client 6 for RHEL 8 x86_64 - Update Services for SAP Solutions (RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-client/6/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[pipelines-1.10-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Pipelines 1.10 (for RHEL 8 x86_64) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.10/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.3-for-rhel-8-x86_64-source-rpms] name = Red Hat Ansible Automation Platform 2.3 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhwa-nhc-1-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Workload Availability - Node Healthcheck Operator 1 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-nhc/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[rhwa-nmo-1-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Workload Availability - Node Maintenance Operator 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-nmo/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/redhat.repo[quarkus-2.2-for-rhel-8-x86_64-debug-rpms] name = Red Hat build of Quarkus 2.2 (RHEL 8) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/quarkus/2.2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
... and 723 more items.

verify no repo in /etc/yum.repos.d has gpgcheck disabled  oval:ssg-test_enable_gpgcheck_for_all_repositories_no_disabled:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/yum.repos.d/redhat.repo [network-observability-0.1.3-for-rhel-8-x86_64-files] name = Network Observability (Files) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/NETOBSERV/1.0/files enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[openshift-serverless-1-for-rhel-8-x86_64-source-rpms] name = Red Hat Openshift Serverless 1 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhoss/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[lvms-4.14-for-rhel-8-x86_64-debug-rpms] name = Logical Volume Manager Storage 4.14 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/lvms/4.14/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.13-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.13 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.16-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.16 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.16/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.14-for-rhel-8-x86_64-debug-rpms] name = OpenShift Developer Tools and Services 4.14 (RHEL 8) (x86_64 Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.14/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[openshift-builds-1.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Builds 1.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openshift-builds/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhosds-textonly-3-for-middleware-rpms] name = Red Hat OpenShift Dev Spaces 3 Container Advisories baseurl = https://cdn.redhat.com/content/dist/middleware/rhosds/3.0/x86_64/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.18-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.18 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.18/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.6-for-rhel-8-x86_64-source-rpms] name = OpenShift Developer Tools and Services 4.6 (RHEL 8) (x86_64 Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.7/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.12-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.12 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.12/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.15-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 4.15 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.15/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
true/etc/yum.repos.d/local-appstream.repo[local-AppStream] name=Local AppStream baseurl=file:///opt/localrepo/AppStream enabled=1 gpgcheck=0
false/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.4-for-rhel-8-x86_64-rpms] name = Red Hat Ansible Automation Platform 2.4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[quarkus-1-for-rhel-8-x86_64-rpms] name = Red Hat build of Quarkus 1 (RHEL 8) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/quarkus/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.9-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.9 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-2.5-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 2.5 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.5/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.1-for-rhel-8-x86_64-debug-rpms] name = OpenShift Pipelines 1.1 (RHEL 8) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[discovery-1-for-rhel-8-x86_64-rpms] name = Red Hat Discovery 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/discovery/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[amq-clients-3-for-rhel-8-x86_64-source-rpms] name = Red Hat AMQ Clients 3 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/amq/3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.15-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.15 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.13-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift GitOps 1.13 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.13/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.9-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.9 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.9/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift GitOps 1.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.8-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 4.8 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.8/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.12-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Container Platform 4.12 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.12/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[automation-hub-4.2-for-rhel-8-x86_64-source-rpms] name = Red Hat Automation Hub 4.2 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/automation-hub/4.2/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-eus-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.7/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[openstack-15-tools-for-rhel-8-x86_64-rpms] name = Red Hat OpenStack Platform 15 Tools for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openstack-tools/15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.5-for-rhel-8-x86_64-rpms] name = OpenShift Tools and Services 4.5 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.5/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.15-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.15 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.15/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.3-for-rhel-8-x86_64-source-rpms] name = OpenShift Tools and Services 4.3 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhods-1.20-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Data Science 1.20 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhods/1.20/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[application-interconnect-1-for-rhel-8-x86_64-source-rpms] name = Red Hat Application Interconnect for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhai/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[interconnect-2-for-rhel-8-x86_64-debug-rpms] name = JBoss Interconnect 2 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/interconnect/2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.3-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Pipelines 1.3 (for RHEL 8 x86_64) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.3/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.9-for-rhel-8-x86_64-eus-debug-rpms] name = Red Hat Satellite Tools 6.9 for RHEL 8 x86_64 - Extended Update Support (Debug RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-6-client-2-for-rhel-8-x86_64-rpms] name = Red Hat Satellite 6 Client 2 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-client-2/6/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.15-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.15 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.15/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.6-for-rhel-8-x86_64-e4s-source-rpms] name = Red Hat Satellite Tools 6.6 for RHEL 8 x86_64 - Update Services SAP Solutions (Source RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.16-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift GitOps 1.16 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.16/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.7-for-rhel-8-x86_64-e4s-source-rpms] name = Red Hat Satellite Tools 6.7 for RHEL 8 x86_64 - Update Services SAP Solutions (Source RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.7/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.13-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.13 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.12-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Pipelines 1.12 (for RHEL 8 x86_64) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.12/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.1-for-rhel-8-x86_64-source-rpms] name = OpenShift Pipelines 1.1 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhel-8-for-x86_64-baseos-eus-source-rpms] name = Red Hat Enterprise Linux 8 for x86_64 - BaseOS - Extended Update Support (Source RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/baseos/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.12-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 4.12 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.12/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rodoo-1-for-rhel-8-x86_64-rpms] name = Run Once Duration Override Operator (RODOO) 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rodoo/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.19-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.19 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.19/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhoss-1.29-for-rhel-8-x86_64-source-rpms] name = Red Hat Openshift Serverless 1.29 (RHEL 8) (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhoss/1.29/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.9-for-rhel-8-x86_64-debug-rpms] name = OpenShift Developer Tools and Services 4.9 (RHEL 8) (x86_64 Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.9/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhosdt-2-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift distributed tracing 2 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosdt/2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhel-atomic-7-cdk-3.3-rpms] name = Red Hat Container Development Kit 3.3 /(RPMs) baseurl = https://cdn.redhat.com/content/dist/rhel/atomic/7/7Server/$basearch/cdk/3.3/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.13-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 4.13 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.6-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.6 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rh-gluster-3-client-for-rhel-8-x86_64-debug-rpms] name = Red Hat Storage Native Client for RHEL 8 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhgs-client/3.5/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.9-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6.9 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.9/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-2.2-for-rhel-8-x86_64-source-rpms] name = Red Hat Container Native Virtualization 2.2 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.2/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhosj-1.24-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Jaeger 1.24 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosj/1.24/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.13-for-rhel-8-x86_64-source-rpms] name = OpenShift Developer Tools and Services 4.13 (RHEL 8) (x86_64 Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.13/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat Ansible Automation Platform 2.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.4-for-rhel-8-x86_64-rpms] name = OpenShift Tools and Services 4.4 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-utils-6.15-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Utils 6.15 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-utils/6.15/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[jb-coreservices-1-for-rhel-8-x86_64-rhui-debug-rpms] name = JBoss Core Services (RHEL 8) (Debug RPMs) from RHUI baseurl = https://cdn.redhat.com/content/dist/layered/rhui/rhel8/x86_64/jbcs/1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.8-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6.8 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-tools/6.8/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.18-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 4.18 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.18/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-utils-6.11-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Utils 6.11 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/sat-utils/6.11/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.7-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Pipelines 1.7 (for RHEL 8 x86_64) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.7/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.1-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[discovery-1-for-rhel-8-x86_64-debug-rpms] name = Red Hat Discovery 1 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/discovery/1/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[jb-coreservices-1-for-rhel-8-x86_64-rpms] name = JBoss Core Services (RHEL 8) (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/jbcs/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.5-for-rhel-8-x86_64-e4s-rpms] name = Red Hat Satellite Tools 6.5 for RHEL 8 x86_64 - Update Services SAP Solutions (RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-tools/6.5/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhmtc-for-rhel-8-x86_64-rpms] name = Red Hat Migration Toolkit for Containers for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhmtc/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.11-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift GitOps 1.11 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.11/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhosj-1.20-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Jaeger 1.20.0 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhosj/1.20/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.4-for-rhel-8-x86_64-source-rpms] name = Red Hat Ansible Automation Platform 2.4 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.4/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[gitops-1.8-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift GitOps 1.8 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/gitops/1.8/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6.10-for-rhel-8-x86_64-eus-rpms] name = Red Hat Satellite Tools 6.10 for RHEL 8 x86_64 - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/sat-tools/6.10/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.13-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.13 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.13/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhwa-snr-1-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Workload Availability - Self Node Remediation 1 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-snr/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-4.10-for-rhel-8-x86_64-debug-rpms] name = Red Hat Container Native Virtualization 4.10 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/4.10/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ocp-tools-4.8-for-rhel-8-x86_64-rpms] name = OpenShift Developer Tools and Services 4.8 (RHEL 8) (x86_64 RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ocp-tools/4.8/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[openstack-15-tools-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenStack Platform 15 Tools for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/openstack-tools/15/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6-beta-for-rhel-8-x86_64-source-rpms] name = Red Hat Satellite Tools 6 Beta for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/sat-tools/6/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-tools-6-beta-for-rhel-8-x86_64-debug-rpms] name = Red Hat Satellite Tools 6 Beta for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/sat-tools/6/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhel-8-for-x86_64-baseos-eus-rpms] name = Red Hat Enterprise Linux 8 for x86_64 - BaseOS - Extended Update Support (RPMs) baseurl = https://cdn.redhat.com/content/eus/rhel8/$releasever/x86_64/baseos/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[jb-eap-7.4-for-rhel-8-x86_64-rhui-debug-rpms] name = JBoss Enterprise Application Platform 7.4 (RHEL 8) (Debug RPMs) from RHUI baseurl = https://cdn.redhat.com/content/dist/layered/rhui/rhel8/x86_64/jbeap/7.4/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-6-client-2-for-rhel-8-x86_64-e4s-debug-rpms] name = Red Hat Satellite 6 Client 2 for RHEL 8 x86_64 - Update Services SAP Solutions (Debug RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-client-2/6/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhv-4-tools-beta-for-rhel-8-x86_64-debug-rpms] name = Red Hat Virtualization 4 Tools Beta for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/beta/layered/rhel8/x86_64/rhv-tools/4/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.16-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Container Platform 4.16 for RHEL 8 x86_64 (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.16/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[cnv-2.4-for-rhel-8-x86_64-rpms] name = Red Hat Container Native Virtualization 2.4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/cnv/2.4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release,file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhocp-4.10-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Container Platform 4.10 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhocp/4.10/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhceph-4-tools-for-rhel-8-x86_64-rpms] name = Red Hat Ceph Storage Tools 4 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhceph-tools/4/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[satellite-client-6-for-rhel-8-x86_64-e4s-rpms] name = Red Hat Satellite Client 6 for RHEL 8 x86_64 - Update Services for SAP Solutions (RPMs) baseurl = https://cdn.redhat.com/content/e4s/rhel8/$releasever/x86_64/sat-client/6/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[pipelines-1.10-for-rhel-8-x86_64-debug-rpms] name = Red Hat OpenShift Pipelines 1.10 (for RHEL 8 x86_64) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/pipelines/1.10/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[ansible-automation-platform-2.3-for-rhel-8-x86_64-source-rpms] name = Red Hat Ansible Automation Platform 2.3 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/ansible-automation-platform/2.3/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhwa-nhc-1-for-rhel-8-x86_64-source-rpms] name = Red Hat OpenShift Workload Availability - Node Healthcheck Operator 1 for RHEL 8 x86_64 (Source RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-nhc/1/source/SRPMS enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[rhwa-nmo-1-for-rhel-8-x86_64-rpms] name = Red Hat OpenShift Workload Availability - Node Maintenance Operator 1 for RHEL 8 x86_64 (RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/rhwa-nmo/1/os enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
false/etc/yum.repos.d/redhat.repo[quarkus-2.2-for-rhel-8-x86_64-debug-rpms] name = Red Hat build of Quarkus 2.2 (RHEL 8) (Debug RPMs) baseurl = https://cdn.redhat.com/content/dist/layered/rhel8/x86_64/quarkus/2.2/debug enabled = 0 gpgcheck = 1 gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release sslverify = 1 sslcacert = /etc/rhsm/ca/redhat-uep.pem sslclientkey = /etc/pki/entitlement/8400969226842797926-key.pem sslclientcert = /etc/pki/entitlement/8400969226842797926.pem sslverifystatus = 1 metadata_expire = 86400 enabled_metadata = 0
... and 723 more items.
Ensure gpgcheck Enabled for Local Packagesxccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages highCCE-80791-7

Ensure gpgcheck Enabled for Local Packages

Rule IDxccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-ensure_gpgcheck_local_packages:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80791-7

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.4.8
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistCM-11(a), CM-11(b), CM-6(a), CM-5(3), SA-12, SA-12(10)
nist-csfPR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59
stigidRHEL-08-010371
stigrefSV-230265r1017378_rule
Description
yum should be configured to verify the signature(s) of local packages prior to installation. To configure yum to verify signatures of local packages, set the localpkg_gpgcheck to 1 in /etc/yum.conf.
Rationale
Changes to any software components can have significant effects to the overall security of the operating system. This requirement ensures the software has not been tampered and has been provided by a trusted vendor.

Accordingly, patches, service packs, device drivers, or operating system components must be signed with a certificate recognized and approved by the organization.

# Remediation is applicable only in certain platforms
if rpm --quiet -q yum; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^localpkg_gpgcheck")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^localpkg_gpgcheck\\>" "/etc/yum.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^localpkg_gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/yum.conf"
else
    if [[ -s "/etc/yum.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/yum.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/yum.conf"
    fi
    cce="CCE-80791-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/yum.conf" >> "/etc/yum.conf"
    printf '%s\n' "$formatted_output" >> "/etc/yum.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80791-7
  - DISA-STIG-RHEL-08-010371
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - ensure_gpgcheck_local_packages
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy

- name: Ensure GPG check Enabled for Local Packages (yum)
  block:

  - name: Check stats of yum
    ansible.builtin.stat:
      path: /etc/yum.conf
    register: pkg

  - name: Check if config file of yum is a symlink
    ansible.builtin.set_fact:
      pkg_config_file_symlink: '{{ pkg.stat.lnk_target if pkg.stat.lnk_target is match("^/.*")
        else "/etc/yum.conf" | dirname ~ "/" ~ pkg.stat.lnk_target }}'
    when: pkg.stat.lnk_target is defined

  - name: Ensure GPG check Enabled for Local Packages (yum)
    community.general.ini_file:
      dest: '{{ pkg_config_file_symlink |  default("/etc/yum.conf") }}'
      section: main
      option: localpkg_gpgcheck
      value: 1
      no_extra_spaces: true
      create: true
  when: '"yum" in ansible_facts.packages'
  tags:
  - CCE-80791-7
  - DISA-STIG-RHEL-08-010371
  - NIST-800-171-3.4.8
  - NIST-800-53-CM-11(a)
  - NIST-800-53-CM-11(b)
  - NIST-800-53-CM-5(3)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SA-12
  - NIST-800-53-SA-12(10)
  - ensure_gpgcheck_local_packages
  - high_severity
  - low_complexity
  - medium_disruption
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

check value of localpkg_gpgcheck in /etc/yum.conf  oval:ssg-test_yum_ensure_gpgcheck_local_packages:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_yum_ensure_gpgcheck_local_packages:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/yum.conf^\s*localpkg_gpgcheck\s*=\s*(1|True|yes)\s*$1
Ensure Red Hat GPG Key Installedxccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed highCCE-80795-8

Ensure Red Hat GPG Key Installed

Rule IDxccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-ensure_redhat_gpgkey_installed:def:1
Time2025-11-18T17:05:00-05:00
Severityhigh
Identifiers:

CCE-80795-8

References:
cis-csc11, 2, 3, 9
cjis5.10.4.1
cobit5APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02
cui3.4.8
hipaa164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i)
isa-62443-20094.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4
isa-62443-2013SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6
iso27001-2013A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4
nerc-cipCIP-003-8 R4.2, CIP-003-8 R6, CIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-5(3), SI-7, SC-12, SC-12(3), CM-6(a)
nist-csfPR.DS-6, PR.DS-8, PR.IP-1
osppFPT_TUD_EXT.1, FPT_TUD_EXT.2
pcidssReq-6.2
os-srgSRG-OS-000366-GPOS-00153
anssiR59
pcidss46.3.3, 6.3
stigidRHEL-08-010019
stigrefSV-256973r1017373_rule
Description
To ensure the system can cryptographically verify base software packages come from Red Hat (and to connect to the Red Hat Network to receive them), the Red Hat GPG key must properly be installed. To install the Red Hat GPG key, run:
$ sudo subscription-manager register
If the system is not connected to the Internet or an RHN Satellite, then install the Red Hat GPG key from trusted media such as the Red Hat installation CD-ROM or DVD. Assuming the disc is mounted in /media/cdrom, use the following command as the root user to import it into the keyring:
$ sudo rpm --import /media/cdrom/RPM-GPG-KEY
Alternatively, the key may be pre-loaded during the RHEL installation. In such cases, the key can be installed by running the following command:
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
Rationale
Changes to software components can have significant effects on the overall security of the operating system. This requirement ensures the software has not been tampered with and that it has been provided by a trusted vendor. The Red Hat GPG key is necessary to cryptographically verify packages are from Red Hat.
OVAL test results details

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

installed OS part of unix family  oval:ssg-test_rhel8_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

redhat-release is version 8  oval:ssg-test_rhel8:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
trueredhat-releasex86_64(none)0.2.el88.100:8.10-0.2.el8199e2f91fd431d51redhat-release-0:8.10-0.2.el8.x86_64

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

oraclelinux-release is version 8  oval:ssg-test_ol8_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ol8_system:obj:1 of type rpminfo_object
Name
oraclelinux-release

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

redhat-release-virtualization-host RPM package is installed  oval:ssg-test_rhvh4_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhvh4_version:obj:1 of type rpminfo_object
Name
redhat-release-virtualization-host

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

RHEVH base RHEL is version 8  oval:ssg-test_rhevh_rhel8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rhevh_rhel8_version:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/redhat-release^Red Hat Enterprise Linux release (\d)\.\d+$1

Red Hat release key package is installed  oval:ssg-test_redhat_package_gpgkey-fd431d51-4ae0493b_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
falsegpg-pubkey(none)(none)5b32db75d40827920:d4082792-5b32db750gpg-pubkey-0:d4082792-5b32db75.(none)
truegpg-pubkey(none)(none)4ae0493bfd431d510:fd431d51-4ae0493b0gpg-pubkey-0:fd431d51-4ae0493b.(none)

Red Hat auxiliary key package is installed  oval:ssg-test_redhat_package_gpgkey-d4082792-5b32db75_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
truegpg-pubkey(none)(none)5b32db75d40827920:d4082792-5b32db750gpg-pubkey-0:d4082792-5b32db75.(none)
falsegpg-pubkey(none)(none)4ae0493bfd431d510:fd431d51-4ae0493b0gpg-pubkey-0:fd431d51-4ae0493b.(none)

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Test installed OS is part of the unix family  oval:ssg-test_unix_family:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFamily
trueunix

Check os-release ID  oval:ssg-test_centos8_name:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/os-releaseID="rhel"

Check os-release VERSION_ID  oval:ssg-test_centos8_version:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_version_centos8:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/os-release^VERSION_ID="(\d)"$1

CentOS8 key package is installed  oval:ssg-test_redhat_package_gpgkey-8483c65d-5ccc5b19_installed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
falsegpg-pubkey(none)(none)5b32db75d40827920:d4082792-5b32db750gpg-pubkey-0:d4082792-5b32db75.(none)
falsegpg-pubkey(none)(none)4ae0493bfd431d510:fd431d51-4ae0493b0gpg-pubkey-0:fd431d51-4ae0493b.(none)
Ensure Software Patches Installedxccdf_org.ssgproject.content_rule_security_patches_up_to_date mediumCCE-80865-9

Ensure Software Patches Installed

Rule IDxccdf_org.ssgproject.content_rule_security_patches_up_to_date
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80865-9

References:
cis-csc18, 20, 4
cjis5.10.4.1
cobit5APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02
isa-62443-20094.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9
iso27001-2013A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3
nistSI-2(5), SI-2(c), CM-6(a)
nist-csfID.RA-1, PR.IP-12
osppFMT_MOF_EXT.1
pcidssReq-6.2
os-srgSRG-OS-000480-GPOS-00227
anssiR61
pcidss46.3.3, 6.3
stigidRHEL-08-010010
stigrefSV-230222r1017041_rule
Description
If the system is joined to the Red Hat Network, a Red Hat Satellite Server, or a yum server, run the following command to install updates:
$ sudo yum update
If the system is not configured to use one of these sources, updates (in the form of RPM packages) can be manually downloaded from the Red Hat Network and installed using rpm.

NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy dictates.
Rationale
Installing software updates is a fundamental mitigation against the exploitation of publicly-known vulnerabilities. If the most recent security patches and updates are not installed, unauthorized users may take advantage of weaknesses in the unpatched software. The lack of prompt attention to patching could result in a system compromise.
Warnings
warning  Red Hat Enterprise Linux 8 does not have a corresponding OVAL CVE Feed. Therefore, this will result in a "not checked" result during a scan.
Evaluation messages
info 
No candidate or applicable check found.
Enable GNOME3 Login Warning Bannerxccdf_org.ssgproject.content_rule_dconf_gnome_banner_enabled mediumCCE-80768-5

Enable GNOME3 Login Warning Banner

Rule IDxccdf_org.ssgproject.content_rule_dconf_gnome_banner_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-dconf_gnome_banner_enabled:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80768-5

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.9
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-8(a), AC-8(b), AC-8(c)
nist-csfPR.AC-7
os-srgSRG-OS-000023-GPOS-00006, SRG-OS-000228-GPOS-00088
cis1.8.2
stigidRHEL-08-010049
stigrefSV-244519r1017326_rule
Description
In the default graphical environment, displaying a login warning banner in the GNOME Display Manager's login screen can be enabled on the login screen by setting banner-message-enable to true.

To enable, add or edit banner-message-enable to /etc/dconf/db/gdm.d/00-security-settings. For example:
[org/gnome/login-screen]
banner-message-enable=true
Once the setting has been added, add a lock to /etc/dconf/db/gdm.d/locks/00-security-settings-lock to prevent user modification. For example:
/org/gnome/login-screen/banner-message-enable
After the settings have been set, run dconf update. The banner text must also be set.
Rationale
Display of a standardized and approved use notification before granting access to the operating system ensures privacy and security notification verbiage used is consistent with applicable federal laws, Executive Orders, directives, policies, regulations, standards, and guidance.

For U.S. Government systems, system use notifications are required only for access via login interfaces with human users and are not required when such human interfaces do not exist.

# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm; then

# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/login-screen\\]" "/etc/dconf/db/" \
                                | grep -v 'distro\|ibus\|gdm.d' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/gdm.d/00-security-settings"
DBDIR="/etc/dconf/db/gdm.d"

mkdir -p "${DBDIR}"

# Comment out the configurations in databases different from the target one
if [ "${#SETTINGSFILES[@]}" -ne 0 ]
then
    if grep -q "^\\s*banner-message-enable\\s*=" "${SETTINGSFILES[@]}"
    then
        
        sed -Ei "s/(^\s*)banner-message-enable(\s*=)/#\1banner-message-enable\2/g" "${SETTINGSFILES[@]}"
    fi
fi

[ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}"
if ! grep -q "\\[org/gnome/login-screen\\]" "${DCONFFILE}"
then
    printf '%s\n' "[org/gnome/login-screen]" >> ${DCONFFILE}
fi

escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*banner-message-enable\\s*=" "${DCONFFILE}"
then
        sed -i "s/\\s*banner-message-enable\\s*=\\s*.*/banner-message-enable=${escaped_value}/g" "${DCONFFILE}"
    else
        sed -i "\\|\\[org/gnome/login-screen\\]|a\\banner-message-enable=${escaped_value}" "${DCONFFILE}"
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/login-screen/banner-message-enable$" "/etc/dconf/db/" \
            | grep -v 'distro\|ibus\|gdm.d' | grep ":" | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/gdm.d/locks"

mkdir -p "${LOCKSFOLDER}"

# Comment out the configurations in databases different from the target one
if [[ ! -z "${LOCKFILES}" ]]
then
    sed -i -E "s|^/org/gnome/login-screen/banner-message-enable$|#&|" "${LOCKFILES[@]}"
fi

if ! grep -qr "^/org/gnome/login-screen/banner-message-enable$" /etc/dconf/db/gdm.d/
then
    echo "/org/gnome/login-screen/banner-message-enable" >> "/etc/dconf/db/gdm.d/locks/00-security-settings-lock"
fi
dconf update

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80768-5
  - DISA-STIG-RHEL-08-010049
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Enable GNOME3 Login Warning Banner
  community.general.ini_file:
    dest: /etc/dconf/db/gdm.d/00-security-settings
    section: org/gnome/login-screen
    option: banner-message-enable
    value: 'true'
    create: true
    no_extra_spaces: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80768-5
  - DISA-STIG-RHEL-08-010049
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Prevent user modification of GNOME banner-message-enabled
  ansible.builtin.lineinfile:
    path: /etc/dconf/db/gdm.d/locks/00-security-settings-lock
    regexp: ^/org/gnome/login-screen/banner-message-enable$
    line: /org/gnome/login-screen/banner-message-enable
    create: true
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80768-5
  - DISA-STIG-RHEL-08-010049
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

- name: Dconf Update
  ansible.builtin.command: dconf update
  when: '"gdm" in ansible_facts.packages'
  tags:
  - CCE-80768-5
  - DISA-STIG-RHEL-08-010049
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(b)
  - NIST-800-53-AC-8(c)
  - dconf_gnome_banner_enabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
OVAL test results details

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

package dconf is installed  oval:ssg-test_package_dconf_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluateddconfx86_64(none)4.el80.28.00:0.28.0-4.el8199e2f91fd431d51dconf-0:0.28.0-4.el8.x86_64

dconf user profile exists  oval:ssg-test_dconf_user_profile:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/dconf/profile/useruser-db:user system-db:local

GUI banner is enabled  oval:ssg-test_banner_gui_enabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_banner_gui_enabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/gdm.d/^.*$^\[org/gnome/login-screen\]([^\n]*\n+)+?banner-message-enable=true$1

GUI banner cannot be changed by user  oval:ssg-test_prevent_user_banner_gui_enabled_change:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_prevent_user_banner_gui_enabled_change:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/dconf/db/gdm.d/locks/^.*$^/org/gnome/login-screen/banner-message-enable$1
Modify the System Login Bannerxccdf_org.ssgproject.content_rule_banner_etc_issue mediumCCE-80763-6

Modify the System Login Banner

Rule IDxccdf_org.ssgproject.content_rule_banner_etc_issue
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-banner_etc_issue:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80763-6

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.9
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-8(a), AC-8(c)
nist-csfPR.AC-7
os-srgSRG-OS-000023-GPOS-00006, SRG-OS-000228-GPOS-00088
stigidRHEL-08-010060
stigrefSV-230227r1017046_rule
Description
To configure the system login banner edit /etc/issue. Replace the default text with a message compliant with the local site policy or a legal disclaimer. The DoD required text is either:

You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions:
-The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations.
-At any time, the USG may inspect and seize data stored on this IS.
-Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose.
-This IS includes security measures (e.g., authentication and access controls) to protect USG interests -- not for your personal benefit or privacy.
-Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details.


OR:

I've read & consent to terms in IS user agreem't.
Rationale
Display of a standardized and approved use notification before granting access to the operating system ensures privacy and security notification verbiage used is consistent with applicable federal laws, Executive Orders, directives, policies, regulations, standards, and guidance.

System use notifications are required only for access via login interfaces with human users and are not required when such human interfaces do not exist.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

login_banner_text="^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\),[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to,[\s\n]+penetration[\s\n]+testing,[\s\n]+COMSEC[\s\n]+monitoring,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\),[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\),[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring,[\s\n]+interception,[\s\n]+and[\s\n]+search,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications,[\s\n]+or[\s\n]+work[\s\n]+product,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys,[\s\n]+psychotherapists,[\s\n]+or[\s\n]+clergy,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$"

# Multiple regexes transform the banner regex into a usable banner
# 0 - Remove anchors around the banner text
login_banner_text=$(echo "$login_banner_text" | sed 's/^\^\(.*\)\$$/\1/g')
# 1 - Keep only the first banners if there are multiple
#    (dod_banners contains the long and short banner)
login_banner_text=$(echo "$login_banner_text" | sed 's/^(\(.*\.\)|.*)$/\1/g')
# 2 - Add spaces ' '. (Transforms regex for "space or newline" into a " ")
login_banner_text=$(echo "$login_banner_text" | sed 's/\[\\s\\n\]+/ /g')
# 3 - Adds newlines. (Transforms "(?:\[\\n\]+|(?:\\n)+)" into "\n")
login_banner_text=$(echo "$login_banner_text" | sed 's/(?:\[\\n\]+|(?:\\n)+)/\n/g')
# 4 - Remove any leftover backslash. (From any parethesis in the banner, for example).
login_banner_text=$(echo "$login_banner_text" | sed 's/\\//g')
formatted=$(echo "$login_banner_text" | fold -sw 80)
cat <<EOF >/etc/issue
$formatted
EOF

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:unknown
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80763-6
  - DISA-STIG-RHEL-08-010060
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - banner_etc_issue
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy
- name: XCCDF Value login_banner_text # promote to variable
  set_fact:
    login_banner_text: !!str ^(You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U\.S\.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG\-authorized[\s\n]+use[\s\n]+only\.[\s\n]+By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\),[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:(?:[\n]+|(?:\\n)+)\-The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to,[\s\n]+penetration[\s\n]+testing,[\s\n]+COMSEC[\s\n]+monitoring,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\),[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\),[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations\.(?:[\n]+|(?:\\n)+)\-At[\s\n]+any[\s\n]+time,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS\.(?:[\n]+|(?:\\n)+)\-Communications[\s\n]+using,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring,[\s\n]+interception,[\s\n]+and[\s\n]+search,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG\-authorized[\s\n]+purpose\.(?:[\n]+|(?:\\n)+)\-This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e\.g\.,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests\-\-not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy\.(?:[\n]+|(?:\\n)+)\-Notwithstanding[\s\n]+the[\s\n]+above,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications,[\s\n]+or[\s\n]+work[\s\n]+product,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys,[\s\n]+psychotherapists,[\s\n]+or[\s\n]+clergy,[\s\n]+and[\s\n]+their[\s\n]+assistants\.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential\.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details\.|I've[\s\n]+read[\s\n]+\&[\s\n]+consent[\s\n]+to[\s\n]+terms[\s\n]+in[\s\n]+IS[\s\n]+user[\s\n]+agreem't\.)$
  tags:
    - always

- name: Modify the System Login Banner - Ensure Correct Banner
  ansible.builtin.copy:
    dest: /etc/issue
    content: '{{ login_banner_text | regex_replace("^\^(.*)\$$", "\1") | regex_replace("^\((.*\.)\|.*\)$",
      "\1") | regex_replace("\[\\s\\n\]\+"," ") | regex_replace("\(\?:\[\\n\]\+\|\(\?:\\\\n\)\+\)",
      "\n") | regex_replace("\\", "") | wordwrap() }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80763-6
  - DISA-STIG-RHEL-08-010060
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - banner_etc_issue
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - unknown_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: master
    machineconfiguration.openshift.io/role: worker
  name: 75-banner-etc-issue
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,You%20are%20accessing%20a%20U.S.%20Government%20%28USG%29%20Information%20System%20%28IS%29%20that%20is%20%0Aprovided%20for%20USG-authorized%20use%20only.%20By%20using%20this%20IS%20%28which%20includes%20any%20%0Adevice%20attached%20to%20this%20IS%29%2C%20you%20consent%20to%20the%20following%20conditions%3A%0A%0A-The%20USG%20routinely%20intercepts%20and%20monitors%20communications%20on%20this%20IS%20for%20%0Apurposes%20including%2C%20but%20not%20limited%20to%2C%20penetration%20testing%2C%20COMSEC%20monitoring%2C%20%0Anetwork%20operations%20and%20defense%2C%20personnel%20misconduct%20%28PM%29%2C%20law%20enforcement%20%0A%28LE%29%2C%20and%20counterintelligence%20%28CI%29%20investigations.%0A%0A-At%20any%20time%2C%20the%20USG%20may%20inspect%20and%20seize%20data%20stored%20on%20this%20IS.%0A%0A-Communications%20using%2C%20or%20data%20stored%20on%2C%20this%20IS%20are%20not%20private%2C%20are%20subject%20%0Ato%20routine%20monitoring%2C%20interception%2C%20and%20search%2C%20and%20may%20be%20disclosed%20or%20used%20%0Afor%20any%20USG-authorized%20purpose.%0A%0A-This%20IS%20includes%20security%20measures%20%28e.g.%2C%20authentication%20and%20access%20controls%29%20%0Ato%20protect%20USG%20interests--not%20for%20your%20personal%20benefit%20or%20privacy.%0A%0A-Notwithstanding%20the%20above%2C%20using%20this%20IS%20does%20not%20constitute%20consent%20to%20PM%2C%20LE%20%0Aor%20CI%20investigative%20searching%20or%20monitoring%20of%20the%20content%20of%20privileged%20%0Acommunications%2C%20or%20work%20product%2C%20related%20to%20personal%20representation%20or%20services%20%0Aby%20attorneys%2C%20psychotherapists%2C%20or%20clergy%2C%20and%20their%20assistants.%20Such%20%0Acommunications%20and%20work%20product%20are%20private%20and%20confidential.%20See%20User%20%0AAgreement%20for%20details.
        mode: 0644
        path: /etc/issue.d/legal-notice
        overwrite: true
OVAL test results details

correct banner in /etc/issue  oval:ssg-test_banner_etc_issue:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/issue\S Kernel \r on an \m
false/etc/issue.d/cockpit.issueActivate the web console with: systemctl enable --now cockpit.socket
Account Lockouts Must Be Loggedxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_audit mediumCCE-86099-9

Account Lockouts Must Be Logged

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_audit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_audit:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86099-9

References:
nistAC-7 (a)
os-srgSRG-OS-000021-GPOS-00005
stigidRHEL-08-020021
stigrefSV-230343r1017155_rule
Description
PAM faillock locks an account due to excessive password failures, this event must be logged.
Rationale
Without auditing of these events it may be harder or impossible to identify what an attacker did after an attack.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*audit"
    line="audit"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\baudit\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\baudit\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*audit' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ audit/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Check if system relies on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Remediation where authselect tool is present
  block:

  - name: Account Lockouts Must Be Logged - Check integrity of authselect current
      profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Account Lockouts Must Be Logged - Informative message based on the authselect
      integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Account Lockouts Must Be Logged - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Account Lockouts Must Be Logged - Ensure "with-faillock" feature is enabled
      using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Remediation where authselect tool is not
    present
  block:

  - name: Account Lockouts Must Be Logged - Check if pam_faillock.so is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Account Lockouts Must Be Logged - Enable pam_faillock.so preauth editing
      PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Account Lockouts Must Be Logged - Enable pam_faillock.so authfail editing
      PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Account Lockouts Must Be Logged - Enable pam_faillock.so account section
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter
    in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*audit
    line: audit
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter
    not in PAM files
  block:

  - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/system-auth file is
      present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Account Lockouts Must Be Logged - Check the proper remediation for the system
    block:

    - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as
        a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Account Lockouts Must Be Logged - Check if system relies on authselect
        tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Account Lockouts Must Be Logged - Ensure authselect custom profile is
        used if authselect is present
      block:

      - name: Account Lockouts Must Be Logged - Check integrity of authselect current
          profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Account Lockouts Must Be Logged - Informative message based on the authselect
          integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Account Lockouts Must Be Logged - Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Account Lockouts Must Be Logged - Define the current authselect profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Account Lockouts Must Be Logged - Define the new authselect custom profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Account Lockouts Must Be Logged - Get authselect current features to
          also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Account Lockouts Must Be Logged - Check if any custom profile with the
          same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Account Lockouts Must Be Logged - Create an authselect custom profile
          based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Account Lockouts Must Be Logged - Create an authselect custom profile
          based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile
          is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Account Lockouts Must Be Logged - Restore the authselect features in
          the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according
          to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Account Lockouts Must Be Logged - Define a fact for control already filtered
        in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Account Lockouts Must Be Logged - Check if {{ pam_file_path }} file is
        present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so"
        is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Account Lockouts Must Be Logged - Check if /etc/pam.d/password-auth file
      is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Account Lockouts Must Be Logged - Check the proper remediation for the system
    block:

    - name: Account Lockouts Must Be Logged - Define the PAM file to be edited as
        a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Account Lockouts Must Be Logged - Check if system relies on authselect
        tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Account Lockouts Must Be Logged - Ensure authselect custom profile is
        used if authselect is present
      block:

      - name: Account Lockouts Must Be Logged - Check integrity of authselect current
          profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Account Lockouts Must Be Logged - Informative message based on the authselect
          integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Account Lockouts Must Be Logged - Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Account Lockouts Must Be Logged - Define the current authselect profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Account Lockouts Must Be Logged - Define the new authselect custom profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Account Lockouts Must Be Logged - Get authselect current features to
          also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Account Lockouts Must Be Logged - Check if any custom profile with the
          same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Account Lockouts Must Be Logged - Create an authselect custom profile
          based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Account Lockouts Must Be Logged - Create an authselect custom profile
          based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Account Lockouts Must Be Logged - Ensure the authselect custom profile
          is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Account Lockouts Must Be Logged - Restore the authselect features in
          the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Account Lockouts Must Be Logged - Change the PAM file to be edited according
          to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Account Lockouts Must Be Logged - Define a fact for control already filtered
        in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Account Lockouts Must Be Logged - Check if {{ pam_file_path }} file is
        present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Account Lockouts Must Be Logged - Ensure the "audit" option from "pam_faillock.so"
        is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\baudit\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Account Lockouts Must Be Logged - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Account Lockouts Must Be Logged - Ensure the pam_faillock.so audit parameter
    in PAM files
  block:

  - name: Account Lockouts Must Be Logged - Check if pam_faillock.so audit parameter
      is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*audit
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_audit_parameter_is_present

  - name: Account Lockouts Must Be Logged - Ensure the inclusion of pam_faillock.so
      preauth audit parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 audit
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_audit_parameter_is_present.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-86099-9
  - DISA-STIG-RHEL-08-020021
  - NIST-800-53-AC-7 (a)
  - accounts_passwords_pam_faillock_audit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Check the presence of audit parameter in system-auth  oval:ssg-test_pam_faillock_audit_parameter_system_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_audit_parameter_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit/etc/pam.d/system-auth1

Check the presence of audit parameter in password-auth  oval:ssg-test_pam_faillock_audit_parameter_password_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_audit_parameter_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit/etc/pam.d/password-auth1

Check the absence of audit parameter in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_audit_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_audit_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/faillock.conf^\s*audit1

Check the absence of audit parameter in system-auth  oval:ssg-test_pam_faillock_audit_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_audit_parameter_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit/etc/pam.d/system-auth1

Check the absence of audit parameter in password-auth  oval:ssg-test_pam_faillock_audit_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_audit_parameter_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]preauth[^\n#]*audit/etc/pam.d/password-auth1

Check the expected audit value in in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_audit_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_audit_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/faillock.conf^\s*audit1
Lock Accounts After Failed Password Attemptsxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny mediumCCE-80667-9

Lock Accounts After Failed Password Attempts

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_deny:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80667-9

References:
cis-csc1, 12, 15, 16
cjis5.5.3
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.8
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a)
nist-csfPR.AC-7
osppFIA_AFL.1
pcidssReq-8.1.6
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis4.4.3.1.1
pcidss48.3.4, 8.3
stigidRHEL-08-020011
stigrefSV-230333r1017145_rule
Description
This rule configures the system to lock out accounts after a number of incorrect login attempts using pam_faillock.so. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. Ensure that the file /etc/security/faillock.conf contains the following entry: deny = <count> Where count should be less than or equal to 3 and greater than 0. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Rationale
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_deny='3'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*deny\s*="
    line="deny = $var_accounts_passwords_pam_faillock_deny"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*deny' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"deny"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Check if system relies on authselect
    tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Remediation where authselect
    tool is present
  block:

  - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect
      current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Lock Accounts After Failed Password Attempts - Informative message based
      on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Lock Accounts After Failed Password Attempts - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Lock Accounts After Failed Password Attempts - Ensure "with-faillock" feature
      is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
      are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so preauth
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so authfail
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts After Failed Password Attempts - Enable pam_faillock.so account
      section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_deny # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_deny: !!str 3
  tags:
    - always

- name: Lock Accounts After Failed Password Attempts - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so
    deny parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*deny\s*=
    line: deny = {{ var_accounts_passwords_pam_faillock_deny }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so
    deny parameter not in PAM files
  block:

  - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Lock Accounts After Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Lock Accounts After Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Lock Accounts After Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Lock Accounts After Failed Password Attempts - Informative message based
          on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Lock Accounts After Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Lock Accounts After Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Check if any custom profile
          with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts After Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts After Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts After Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Lock Accounts After Failed Password Attempts - Change the PAM file to
          be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Lock Accounts After Failed Password Attempts - Define a fact for control
        already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Lock Accounts After Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option
        from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Lock Accounts After Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Lock Accounts After Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Lock Accounts After Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Lock Accounts After Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Lock Accounts After Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Lock Accounts After Failed Password Attempts - Informative message based
          on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Lock Accounts After Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Lock Accounts After Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Check if any custom profile
          with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts After Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts After Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts After Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts After Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Lock Accounts After Failed Password Attempts - Change the PAM file to
          be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Lock Accounts After Failed Password Attempts - Define a fact for control
        already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Lock Accounts After Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Lock Accounts After Failed Password Attempts - Ensure the "deny" option
        from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bdeny\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Lock Accounts After Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Lock Accounts After Failed Password Attempts - Ensure the pam_faillock.so
    deny parameter in PAM files
  block:

  - name: Lock Accounts After Failed Password Attempts - Check if pam_faillock.so
      deny parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*deny
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_deny_parameter_is_present

  - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so
      preauth deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found == 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the inclusion of pam_faillock.so
      authfail deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 deny={{ var_accounts_passwords_pam_faillock_deny }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found == 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the desired value
      for pam_faillock.so preauth deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(deny)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found > 0

  - name: Lock Accounts After Failed Password Attempts - Ensure the desired value
      for pam_faillock.so authfail deny parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(deny)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_deny }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_deny_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-80667-9
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020011
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.6
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

no more that one pam_unix.so is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_system_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_system_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/system-auth1

no more that one pam_unix.so is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_password_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_password_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/password-auth1

One and only one occurrence is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_system_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_system_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/system-auth1

One and only one occurrence is expected in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_system_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_system_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/system-auth1

One and only one occurrence is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_password_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_password_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/password-auth1

One and only one occurrence is expected in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_password_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_password_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/password-auth1

Check the expected deny value in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_pamd_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
3
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+)
/etc/pam.d/system-auth1

Check the expected deny value in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_pamd_password:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
3
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+)
/etc/pam.d/password-auth1

Check the absence of deny parameter in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*deny[\s]*=[\s]*([0-9]+)/etc/security/faillock.conf1

Check the absence of deny parameter in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+)/etc/pam.d/system-auth1

Check the absence of deny parameter in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*deny=([0-9]+)/etc/pam.d/password-auth1

Check the expected deny value in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_deny_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
3
^[\s]*deny[\s]*=[\s]*([0-9]+)
/etc/security/faillock.conf1
Configure the root Account for Failed Password Attemptsxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny_root mediumCCE-80668-7

Configure the root Account for Failed Password Attempts

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny_root
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_deny_root:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80668-7

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b), IA-5(c)
nist-csfPR.AC-7
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis4.4.3.1.3
stigidRHEL-08-020023
stigrefSV-230345r1017157_rule
Description
This rule configures the system to lock out the root account after a number of incorrect login attempts using pam_faillock.so. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Rationale
By limiting the number of failed logon attempts, the risk of unauthorized system access via user password guessing, also known as brute-forcing, is reduced. Limits are imposed by locking the account.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*even_deny_root"
    line="even_deny_root"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\beven_deny_root\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*even_deny_root' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ even_deny_root/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ even_deny_root/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Check if system
    relies on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Remediation where
    authselect tool is present
  block:

  - name: Configure the root Account for Failed Password Attempts - Check integrity
      of authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Configure the root Account for Failed Password Attempts - Informative message
      based on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Configure the root Account for Failed Password Attempts - Get authselect
      current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Configure the root Account for Failed Password Attempts - Ensure "with-faillock"
      feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Configure the root Account for Failed Password Attempts - Ensure authselect
      changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Remediation where
    authselect tool is not present
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      preauth editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      authfail editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Configure the root Account for Failed Password Attempts - Enable pam_faillock.so
      account section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Check the presence
    of /etc/security/faillock.conf file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*even_deny_root
    line: even_deny_root
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter not in PAM files
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Configure the root Account for Failed Password Attempts - Check the proper
      remediation for the system
    block:

    - name: Configure the root Account for Failed Password Attempts - Define the PAM
        file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Configure the root Account for Failed Password Attempts - Check if system
        relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Configure the root Account for Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Configure the root Account for Failed Password Attempts - Informative
          message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Configure the root Account for Failed Password Attempts - Define the
          current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Define the
          new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Check if any
          custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Ensure the
          authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Restore the
          authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Configure the root Account for Failed Password Attempts - Change the
          PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Define a fact
        for control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Configure the root Account for Failed Password Attempts - Check if {{
        pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Configure the root Account for Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Configure the root Account for Failed Password Attempts - Check the proper
      remediation for the system
    block:

    - name: Configure the root Account for Failed Password Attempts - Define the PAM
        file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Configure the root Account for Failed Password Attempts - Check if system
        relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Configure the root Account for Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Configure the root Account for Failed Password Attempts - Informative
          message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Configure the root Account for Failed Password Attempts - Define the
          current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Define the
          new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Check if any
          custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Create an
          authselect custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Ensure the
          authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Configure the root Account for Failed Password Attempts - Restore the
          authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Configure the root Account for Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Configure the root Account for Failed Password Attempts - Change the
          PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Define a fact
        for control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Configure the root Account for Failed Password Attempts - Check if {{
        pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Configure the root Account for Failed Password Attempts - Ensure the "even_deny_root"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\beven_deny_root\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Configure the root Account for Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure the root Account for Failed Password Attempts - Ensure the pam_faillock.so
    even_deny_root parameter in PAM files
  block:

  - name: Configure the root Account for Failed Password Attempts - Check if pam_faillock.so
      even_deny_root parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*even_deny_root
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_even_deny_root_parameter_is_present

  - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so preauth even_deny_root parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 even_deny_root
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_even_deny_root_parameter_is_present.found == 0

  - name: Configure the root Account for Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so authfail even_deny_root parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 even_deny_root
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_even_deny_root_parameter_is_present.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-80668-7
  - DISA-STIG-RHEL-08-020023
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(c)
  - accounts_passwords_pam_faillock_deny_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

No more than one pam_unix.so is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_system_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_system_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth\N+pam_unix\.so^/etc/pam.d/system-auth$1

No more than one pam_unix.so is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_password_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_password_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth\N+pam_unix\.so^/etc/pam.d/password-auth$1

One and only one pattern occurrence is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_system_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_system_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail^/etc/pam.d/system-auth$1

One and only one pattern occurrence is expected in account section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_system_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_system_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so^/etc/pam.d/system-auth$1

One and only one pattern occurrence is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_password_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_password_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail^/etc/pam.d/password-auth$1

One and only one pattern occurrence is expected in account section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_password_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_password_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so^/etc/pam.d/password-auth$1

Check the expected even_deny_root parameter in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_pamd_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root^/etc/pam.d/system-auth$1

Check the expected even_deny_root parameter in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_pamd_password:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root^/etc/pam.d/password-auth$1

Check the absence of even_deny_root parameter in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*even_deny_root^/etc/security/faillock.conf$1

Check the absence of even_deny_root parameter in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root^/etc/pam.d/system-auth$1

Check the absence of even_deny_root parameter in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*even_deny_root^/etc/pam.d/password-auth$1

Check the expected even_deny_root parameter in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_deny_root_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_deny_root_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*even_deny_root^/etc/security/faillock.conf$1
Lock Accounts Must Persistxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_dir mediumCCE-86067-6

Lock Accounts Must Persist

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_dir
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_dir:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86067-6

References:
nistAC-7(b), AC-7(a), AC-7.1(ii)
os-srgSRG-OS-000021-GPOS-00005, SRG-OS-000329-GPOS-00128
stigidRHEL-08-020016, RHEL-08-020017
stigrefSV-230338r1017150_rule, SV-230339r1017151_rule
Description
This rule ensures that the system lock out accounts using pam_faillock.so persist after system reboot. From "pam_faillock" man pages:
Note that the default directory that "pam_faillock" uses is usually cleared on system
boot so the access will be reenabled after system reboot. If that is undesirable, a different
tally directory must be set with the "dir" option.
pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version. The chosen profile expects the directory to be /var/log/faillock. To configure the tally directory, add the following line to /etc/security/faillock.conf:
dir = /var/log/faillock
Rationale
Locking out user accounts after a number of incorrect attempts prevents direct password guessing attacks. In combination with the silent option, user enumeration attacks are also mitigated.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_dir='/var/log/faillock'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*dir\s*="
    line="dir = $var_accounts_passwords_pam_faillock_dir"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(dir\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_dir"'|g' $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bdir\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bdir\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*dir' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ dir='"$var_accounts_passwords_pam_faillock_dir"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ dir='"$var_accounts_passwords_pam_faillock_dir"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"dir"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_dir"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"dir"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_dir"'\3/' "$pam_file"
        fi
    done
fi

if ! rpm -q --quiet "python3-libselinux" ; then
    yum install -y "python3-libselinux"
fi
if ! rpm -q --quiet "python3-policycoreutils" ; then
    yum install -y "python3-policycoreutils"
fi
if ! rpm -q --quiet "policycoreutils-python-utils" ; then
    yum install -y "policycoreutils-python-utils"
fi

mkdir -p "$var_accounts_passwords_pam_faillock_dir"
# Workaround for https://github.com/OpenSCAP/openscap/issues/2242: Use full
# path to semanage and restorecon commands to avoid the issue with the command
# not being found.
/usr/sbin/semanage fcontext -a -t faillog_t "$var_accounts_passwords_pam_faillock_dir(/.*)?"
/usr/sbin/restorecon -R -v "$var_accounts_passwords_pam_faillock_dir"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Check if system relies on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Remediation where authselect tool is present
  block:

  - name: Lock Accounts Must Persist - Check integrity of authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Lock Accounts Must Persist - Informative message based on the authselect
      integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Lock Accounts Must Persist - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Lock Accounts Must Persist - Ensure "with-faillock" feature is enabled using
      authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Lock Accounts Must Persist - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Remediation where authselect tool is not present
  block:

  - name: Lock Accounts Must Persist - Check if pam_faillock.so is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Lock Accounts Must Persist - Enable pam_faillock.so preauth editing PAM
      files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts Must Persist - Enable pam_faillock.so authfail editing PAM
      files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Lock Accounts Must Persist - Enable pam_faillock.so account section editing
      PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
- name: XCCDF Value var_accounts_passwords_pam_faillock_dir # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_dir: !!str /var/log/faillock
  tags:
    - always

- name: Lock Accounts Must Persist - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*dir\s*=
    line: dir = {{ var_accounts_passwords_pam_faillock_dir }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter not
    in PAM files
  block:

  - name: Lock Accounts Must Persist - Check if /etc/pam.d/system-auth file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Lock Accounts Must Persist - Check the proper remediation for the system
    block:

    - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local
        fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Lock Accounts Must Persist - Check if system relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts Must Persist - Ensure authselect custom profile is used
        if authselect is present
      block:

      - name: Lock Accounts Must Persist - Check integrity of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Lock Accounts Must Persist - Informative message based on the authselect
          integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Lock Accounts Must Persist - Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Lock Accounts Must Persist - Define the current authselect profile as
          a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Lock Accounts Must Persist - Define the new authselect custom profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Lock Accounts Must Persist - Get authselect current features to also
          enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts Must Persist - Check if any custom profile with the same
          name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts Must Persist - Create an authselect custom profile based
          on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts Must Persist - Create an authselect custom profile based
          on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts Must Persist - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts Must Persist - Ensure the authselect custom profile is
          selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts Must Persist - Restore the authselect features in the
          custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Lock Accounts Must Persist - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Lock Accounts Must Persist - Change the PAM file to be edited according
          to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Lock Accounts Must Persist - Define a fact for control already filtered
        in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Lock Accounts Must Persist - Check if {{ pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so"
        is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Lock Accounts Must Persist - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Lock Accounts Must Persist - Check if /etc/pam.d/password-auth file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Lock Accounts Must Persist - Check the proper remediation for the system
    block:

    - name: Lock Accounts Must Persist - Define the PAM file to be edited as a local
        fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Lock Accounts Must Persist - Check if system relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Lock Accounts Must Persist - Ensure authselect custom profile is used
        if authselect is present
      block:

      - name: Lock Accounts Must Persist - Check integrity of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Lock Accounts Must Persist - Informative message based on the authselect
          integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Lock Accounts Must Persist - Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Lock Accounts Must Persist - Define the current authselect profile as
          a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Lock Accounts Must Persist - Define the new authselect custom profile
          as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Lock Accounts Must Persist - Get authselect current features to also
          enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts Must Persist - Check if any custom profile with the same
          name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Lock Accounts Must Persist - Create an authselect custom profile based
          on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts Must Persist - Create an authselect custom profile based
          on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Lock Accounts Must Persist - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts Must Persist - Ensure the authselect custom profile is
          selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Lock Accounts Must Persist - Restore the authselect features in the
          custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Lock Accounts Must Persist - Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Lock Accounts Must Persist - Change the PAM file to be edited according
          to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Lock Accounts Must Persist - Define a fact for control already filtered
        in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Lock Accounts Must Persist - Check if {{ pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Lock Accounts Must Persist - Ensure the "dir" option from "pam_faillock.so"
        is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bdir\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Lock Accounts Must Persist - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure the pam_faillock.so dir parameter in PAM
    files
  block:

  - name: Lock Accounts Must Persist - Check if pam_faillock.so dir parameter is already
      enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*dir
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_dir_parameter_is_present

  - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so preauth
      dir parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_dir_parameter_is_present.found == 0

  - name: Lock Accounts Must Persist - Ensure the inclusion of pam_faillock.so authfail
      dir parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 dir={{ var_accounts_passwords_pam_faillock_dir }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_dir_parameter_is_present.found == 0

  - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so
      preauth dir parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(dir)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_dir_parameter_is_present.found > 0

  - name: Lock Accounts Must Persist - Ensure the desired value for pam_faillock.so
      authfail dir parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(dir)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_dir }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_dir_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure necessary SELinux packages are installed
  ansible.builtin.package:
    name: '{{ item }}'
    state: present
  with_items:
  - python3-libselinux
  - python3-policycoreutils
  - policycoreutils-python-utils
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Create the tally directory if it does not exist
  ansible.builtin.file:
    path: '{{ var_accounts_passwords_pam_faillock_dir }}'
    state: directory
    setype: faillog_t
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure SELinux file context is permanently set
  ansible.builtin.command:
    cmd: semanage fcontext -a -t faillog_t "{{ var_accounts_passwords_pam_faillock_dir
      }}(/.*)?"
  register: result_accounts_passwords_pam_faillock_dir_semanage
  failed_when: false
  changed_when:
  - result_accounts_passwords_pam_faillock_dir_semanage.rc == 0
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Lock Accounts Must Persist - Ensure SELinux file context is applied
  ansible.builtin.command:
    cmd: restorecon -R "{{ var_accounts_passwords_pam_faillock_dir }}"
  register: result_accounts_passwords_pam_faillock_dir_restorecon
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-86067-6
  - DISA-STIG-RHEL-08-020016
  - DISA-STIG-RHEL-08-020017
  - NIST-800-53-AC-7(a)
  - NIST-800-53-AC-7(b)
  - NIST-800-53-AC-7.1(ii)
  - accounts_passwords_pam_faillock_dir
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
OVAL test results details

Check that the expected dir value in system-auth is present both with preauth and authfail  oval:ssg-test_pam_faillock_dir_parameter_system_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_pam_faillock_dir_parameter_system_auth:obj:1 of type variable_object
Var ref
oval:ssg-var_faillock_dir_set_both_preauth_authfail_system_auth:var:1

Check that the expected dir value in password-auth is present both with preauth and authfail  oval:ssg-test_pam_faillock_dir_parameter_password_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_pam_faillock_dir_parameter_password_auth:obj:1 of type variable_object
Var ref
oval:ssg-var_faillock_dir_set_both_preauth_authfail_password_auth:var:1

Check the absence of dir parameter in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_dir_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_dir_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
dir\s*=\s*(\S+|"[^"]+)
^[\s]*dir\s*=\s*(\S+|"[^"]+)
/etc/security/faillock.conf1

Check the absence of dir parameter in system-auth  oval:ssg-test_pam_faillock_dir_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_dir_parameter_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstanceFilter
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]*dir\s*=\s*(\S+|"[^"]+)
dir\s*=\s*(\S+|"[^"]+)
/etc/pam.d/system-auth1oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1

Check the absence of dir parameter in password-auth  oval:ssg-test_pam_faillock_dir_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_dir_parameter_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstanceFilter
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]*dir\s*=\s*(\S+|"[^"]+)
dir\s*=\s*(\S+|"[^"]+)
/etc/pam.d/password-auth1oval:ssg-state_pam_faillock_dir_parameter_not_default_value:ste:1

Check the expected dir value in in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_dir_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_dir_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
dir\s*=\s*(\S+|"[^"]+)
^[\s]*dir\s*=\s*(\S+|"[^"]+)
/etc/security/faillock.conf1
Set Interval For Counting Failed Password Attemptsxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval mediumCCE-80669-5

Set Interval For Counting Failed Password Attempts

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_interval:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80669-5

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a)
nist-csfPR.AC-7
osppFIA_AFL.1
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
stigidRHEL-08-020012, RHEL-08-020013
stigrefSV-230334r1017146_rule, SV-230335r1017147_rule
Description
Utilizing pam_faillock.so, the fail_interval directive configures the system to lock out an account after a number of incorrect login attempts within a specified time period. Ensure that the file /etc/security/faillock.conf contains the following entry: fail_interval = <interval-in-seconds> where interval-in-seconds is 900 or greater. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Rationale
By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_fail_interval='900'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*fail_interval\s*="
    line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*fail_interval' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"fail_interval"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Check if system relies
    on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect
    tool is present
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check integrity of
      authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Set Interval For Counting Failed Password Attempts - Informative message
      based on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Set Interval For Counting Failed Password Attempts - Get authselect current
      features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Set Interval For Counting Failed Password Attempts - Ensure "with-faillock"
      feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Set Interval For Counting Failed Password Attempts - Ensure authselect changes
      are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      preauth editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      authfail editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Enable pam_faillock.so
      account section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_fail_interval # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_fail_interval: !!str 900
  tags:
    - always

- name: Set Interval For Counting Failed Password Attempts - Check the presence of
    /etc/security/faillock.conf file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*fail_interval\s*=
    line: fail_interval = {{ var_accounts_passwords_pam_faillock_fail_interval }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter not in PAM files
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Interval For Counting Failed Password Attempts - Define the PAM file
        to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Set Interval For Counting Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Set Interval For Counting Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Set Interval For Counting Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Interval For Counting Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Interval For Counting Failed Password Attempts - Define the current
          authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Define the new
          authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Interval For Counting Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Interval For Counting Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Interval For Counting Failed Password Attempts - Change the PAM
          file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Set Interval For Counting Failed Password Attempts - Define a fact for
        control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Set Interval For Counting Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Set Interval For Counting Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Set Interval For Counting Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Interval For Counting Failed Password Attempts - Define the PAM file
        to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Set Interval For Counting Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        custom profile is used if authselect is present
      block:

      - name: Set Interval For Counting Failed Password Attempts - Check integrity
          of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Set Interval For Counting Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Interval For Counting Failed Password Attempts - Get authselect
          current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Interval For Counting Failed Password Attempts - Define the current
          authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Define the new
          authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Get authselect
          current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Interval For Counting Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Interval For Counting Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Interval For Counting Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Interval For Counting Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
          changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Interval For Counting Failed Password Attempts - Change the PAM
          file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Set Interval For Counting Failed Password Attempts - Define a fact for
        control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Set Interval For Counting Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Set Interval For Counting Failed Password Attempts - Ensure the "fail_interval"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bfail_interval\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Set Interval For Counting Failed Password Attempts - Ensure authselect
        changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Interval For Counting Failed Password Attempts - Ensure the pam_faillock.so
    fail_interval parameter in PAM files
  block:

  - name: Set Interval For Counting Failed Password Attempts - Check if pam_faillock.so
      fail_interval parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*fail_interval
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_fail_interval_parameter_is_present

  - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so preauth fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the inclusion
      of pam_faillock.so authfail fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 fail_interval={{ var_accounts_passwords_pam_faillock_fail_interval
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found == 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the desired
      value for pam_faillock.so preauth fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(fail_interval)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found > 0

  - name: Set Interval For Counting Failed Password Attempts - Ensure the desired
      value for pam_faillock.so authfail fail_interval parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(fail_interval)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_fail_interval }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_fail_interval_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-80669-5
  - DISA-STIG-RHEL-08-020012
  - DISA-STIG-RHEL-08-020013
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - accounts_passwords_pam_faillock_interval
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

no more that one pam_unix.so is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_system_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_system_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/system-auth1

no more that one pam_unix.so is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_password_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_password_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/password-auth1

One and only one occurrence is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_system_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_system_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/system-auth1

One and only one occurrence is expected in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_system_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_system_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/system-auth1

One and only one occurrence is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_password_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_password_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/password-auth1

One and only one occurrence is expected in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_password_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_password_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/password-auth1

Check the expected fail_interval value in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
900
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+)
/etc/pam.d/system-auth1

Check the expected fail_interval value in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_password:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
900
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+)
/etc/pam.d/password-auth1

Check the absence of fail_interval parameter in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*fail_interval[\s]*=[\s]*([0-9]+)/etc/security/faillock.conf1

Check the absence of fail_interval parameter in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+)/etc/pam.d/system-auth1

Check the absence of fail_interval parameter in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*fail_interval=([0-9]+)/etc/pam.d/password-auth1

Check the expected fail_interval value in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_fail_interval_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_fail_interval_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
900
^[\s]*fail_interval[\s]*=[\s]*([0-9]+)
/etc/security/faillock.conf1
Do Not Show System Messages When Unsuccessful Logon Attempts Occurxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_silent mediumCCE-87096-4

Do Not Show System Messages When Unsuccessful Logon Attempts Occur

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_silent
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_silent:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-87096-4

References:
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
stigidRHEL-08-020018, RHEL-08-020019
stigrefSV-230340r1017152_rule, SV-230341r1017153_rule
Description
This rule ensures the system prevents informative messages from being presented to the user pertaining to logon information after a number of incorrect login attempts using pam_faillock.so. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version.
Rationale
The pam_faillock module without the silent option will leak information about the existence or non-existence of a user account in the system because the failures are not recorded for unknown users. The message about the user account being locked is never displayed for non-existing user accounts allowing the adversary to infer that a particular account exists or not on the system.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*silent"
    line="silent"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bsilent\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bsilent\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*silent' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ silent/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
    if system relies on authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Remediation
    where authselect tool is present
  block:

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      integrity of authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Informative
      message based on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Get
      authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
      "with-faillock" feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
      authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Remediation
    where authselect tool is not present
  block:

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      if pam_faillock.so is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Enable
      pam_faillock.so preauth editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Enable
      pam_faillock.so authfail editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Enable
      pam_faillock.so account section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
    the presence of /etc/security/faillock.conf file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
    the pam_faillock.so silent parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*silent
    line: silent
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
    the pam_faillock.so silent parameter not in PAM files
  block:

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      if /etc/pam.d/system-auth file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      the proper remediation for the system
    block:

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Define
        the PAM file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
        if system relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        authselect custom profile is used if authselect is present
      block:

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Check integrity of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Informative message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Define the current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Define the new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Get authselect current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Check if any custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Create an authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Create an authselect custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure the authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Restore the authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Change the PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Define
        a fact for control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
        if {{ pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        the "silent" option from "pam_faillock.so" is not present in {{ pam_file_path
        }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bsilent\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      if /etc/pam.d/password-auth file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
      the proper remediation for the system
    block:

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Define
        the PAM file to be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
        if system relies on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        authselect custom profile is used if authselect is present
      block:

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Check integrity of authselect current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Informative message based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Get authselect current profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Define the current authselect profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Define the new authselect custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Get authselect current features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Check if any custom profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Create an authselect custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Create an authselect custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure the authselect custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Restore the authselect features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Ensure authselect changes are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur -
          Change the PAM file to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Define
        a fact for control already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Check
        if {{ pam_file_path }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        the "silent" option from "pam_faillock.so" is not present in {{ pam_file_path
        }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bsilent\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
        authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
    the pam_faillock.so silent parameter in PAM files
  block:

  - name: Do Not Show System Messages When Unsuccessful Logon Attempts Occur - Ensure
      the inclusion of pam_faillock.so preauth silent parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth(:?(?!silent).)*)
      line: \1required\3 silent
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-87096-4
  - DISA-STIG-RHEL-08-020018
  - DISA-STIG-RHEL-08-020019
  - accounts_passwords_pam_faillock_silent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Check the presence of silent parameter in system-auth  oval:ssg-test_pam_faillock_silent_parameter_system_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_silent_parameter_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]+preauth[^\n#]+silent/etc/pam.d/system-auth1

Check the presence of silent parameter in password-auth  oval:ssg-test_pam_faillock_silent_parameter_password_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_silent_parameter_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]+preauth[^\n#]+silent/etc/pam.d/password-auth1

Check the absence of silent parameter in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_silent_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_silent_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/faillock.conf^\s*silent1

Check the absence of silent parameter in system-auth  oval:ssg-test_pam_faillock_silent_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_silent_parameter_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]+preauth[^\n#]+silent/etc/pam.d/system-auth1

Check the absence of silent parameter in password-auth  oval:ssg-test_pam_faillock_silent_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_all_pam_faillock_silent_parameter_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(?:required|requisite)[\s]+pam_faillock.so[^\n#]+preauth[^\n#]+silent/etc/pam.d/password-auth1

Check the expected silent value in in /etc/security/faillock.conf  oval:ssg-test_pam_faillock_silent_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_pam_faillock_silent_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/faillock.conf^\s*silent1
Set Lockout Time for Failed Password Attemptsxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time mediumCCE-80670-3

Set Lockout Time for Failed Password Attempts

Rule IDxccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_passwords_pam_faillock_unlock_time:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80670-3

References:
cis-csc1, 12, 15, 16
cjis5.5.3
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.8
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(b)
nist-csfPR.AC-7
osppFIA_AFL.1
pcidssReq-8.1.7
os-srgSRG-OS-000329-GPOS-00128, SRG-OS-000021-GPOS-00005
anssiR31
cis4.4.3.1.2
pcidss48.3.4, 8.3
stigidRHEL-08-020014, RHEL-08-020015
stigrefSV-230336r1017148_rule, SV-230337r1069292_rule
Description
This rule configures the system to lock out accounts during a specified time period after a number of incorrect login attempts using pam_faillock.so. Ensure that the file /etc/security/faillock.conf contains the following entry: unlock_time=<interval-in-seconds> where interval-in-seconds is 0 or greater. pam_faillock.so module requires multiple entries in pam files. These entries must be carefully defined to work as expected. In order to avoid any errors when manually editing these files, it is recommended to use the appropriate tools, such as authselect or authconfig, depending on the OS version. If unlock_time is set to 0, manual intervention by an administrator is required to unlock a user. This should be done using the faillock tool.
Rationale
By limiting the number of failed logon attempts the risk of unauthorized system access via user password guessing, otherwise known as brute-forcing, is reduced. Limits are imposed by locking the account.
Warnings
warning  If the system supports the new /etc/security/faillock.conf file but the pam_faillock.so parameters are defined directly in /etc/pam.d/system-auth and /etc/pam.d/password-auth, the remediation will migrate the unlock_time parameter to /etc/security/faillock.conf to ensure compatibility with authselect tool. The parameters deny and fail_interval, if used, also have to be migrated by their respective remediation.
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. If the system supports the /etc/security/faillock.conf file, the pam_faillock parameters should be defined in faillock.conf file.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var_accounts_passwords_pam_faillock_unlock_time='0'


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock

authselect apply-changes -b
else
    
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
    if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
        sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth        required      pam_faillock.so preauth silent' "$pam_file"
        sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth        required      pam_faillock.so authfail' "$pam_file"
        sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account     required      pam_faillock.so' "$pam_file"
    fi
    sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required     \3/g' "$pam_file"
done

fi

AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
SKIP_FAILLOCK_CHECK=false

FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then
    regex="^\s*unlock_time\s*="
    line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time"
    if ! grep -q $regex $FAILLOCK_CONF; then
        echo $line >> $FAILLOCK_CONF
    else
        sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF
    fi
    
    for pam_file in "${AUTH_FILES[@]}"
    do
        if [ -e "$pam_file" ] ; then
            PAM_FILE_PATH="$pam_file"
            if [ -f /usr/bin/authselect ]; then
                
                if ! authselect check; then
                echo "
                authselect integrity check failed. Remediation aborted!
                This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
                It is not recommended to manually edit the PAM files when authselect tool is available.
                In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
                exit 1
                fi

                CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
                # If not already in use, a custom profile is created preserving the enabled features.
                if [[ ! $CURRENT_PROFILE == custom/* ]]; then
                    ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
                    # The "local" profile does not contain essential security features required by multiple Benchmarks.
                    # If currently used, it is replaced by "sssd", which is the best option in this case.
                    if [[ $CURRENT_PROFILE == local ]]; then
                        CURRENT_PROFILE="sssd"
                    fi
                    authselect create-profile hardening -b $CURRENT_PROFILE
                    CURRENT_PROFILE="custom/hardening"
                    
                    authselect apply-changes -b --backup=before-hardening-custom-profile
                    authselect select $CURRENT_PROFILE
                    for feature in $ENABLED_FEATURES; do
                        authselect enable-feature $feature;
                    done
                    
                    authselect apply-changes -b --backup=after-hardening-custom-profile
                fi
                PAM_FILE_NAME=$(basename "$pam_file")
                PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

                authselect apply-changes -b
            fi
            
        if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b" "$PAM_FILE_PATH"; then
            sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
        fi
            if [ -f /usr/bin/authselect ]; then
                
                authselect apply-changes -b
            fi
        else
            echo "$pam_file was not found" >&2
        fi
    done
    
else
    for pam_file in "${AUTH_FILES[@]}"
    do
        if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*unlock_time' "$pam_file"; then
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
            sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file"
        else
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"unlock_time"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
            sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file"
        fi
    done
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Check if system relies on
    authselect tool
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect
    tool is present
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
      current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Set Lockout Time for Failed Password Attempts - Informative message based
      on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Set Lockout Time for Failed Password Attempts - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Set Lockout Time for Failed Password Attempts - Ensure "with-faillock" feature
      is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-faillock
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-faillock")

  - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
      are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"pam" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Remediation where authselect
    tool is not present
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so
      is already enabled
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail)
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_is_enabled

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so preauth
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so preauth
      insertbefore: ^auth.*sufficient.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so authfail
      editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: auth        required      pam_faillock.so authfail
      insertbefore: ^auth.*required.*pam_deny\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Enable pam_faillock.so account
      section editing PAM files
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      line: account     required      pam_faillock.so
      insertbefore: ^account.*required.*pam_unix\.so.*
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_is_enabled.found == 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_passwords_pam_faillock_unlock_time # promote to variable
  set_fact:
    var_accounts_passwords_pam_faillock_unlock_time: !!str 0
  tags:
    - always

- name: Set Lockout Time for Failed Password Attempts - Check the presence of /etc/security/faillock.conf
    file
  ansible.builtin.stat:
    path: /etc/security/faillock.conf
  register: result_faillock_conf_check
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter in /etc/security/faillock.conf
  ansible.builtin.lineinfile:
    path: /etc/security/faillock.conf
    regexp: ^\s*unlock_time\s*=
    line: unlock_time = {{ var_accounts_passwords_pam_faillock_unlock_time }}
    state: present
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter not in PAM files
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/system-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/system-auth
    register: result_pam_file_present

  - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/system-auth

    - name: Set Lockout Time for Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Set Lockout Time for Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Lockout Time for Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Change the PAM file
          to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Define a fact for control
        already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Set Lockout Time for Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists

  - name: Set Lockout Time for Failed Password Attempts - Check if /etc/pam.d/password-auth
      file is present
    ansible.builtin.stat:
      path: /etc/pam.d/password-auth
    register: result_pam_file_present

  - name: Set Lockout Time for Failed Password Attempts - Check the proper remediation
      for the system
    block:

    - name: Set Lockout Time for Failed Password Attempts - Define the PAM file to
        be edited as a local fact
      ansible.builtin.set_fact:
        pam_file_path: /etc/pam.d/password-auth

    - name: Set Lockout Time for Failed Password Attempts - Check if system relies
        on authselect tool
      ansible.builtin.stat:
        path: /usr/bin/authselect
      register: result_authselect_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect custom
        profile is used if authselect is present
      block:

      - name: Set Lockout Time for Failed Password Attempts - Check integrity of authselect
          current profile
        ansible.builtin.command:
          cmd: authselect check
        register: result_authselect_check_cmd
        changed_when: false
        check_mode: false
        failed_when: false

      - name: Set Lockout Time for Failed Password Attempts - Informative message
          based on the authselect integrity check result
        ansible.builtin.assert:
          that:
          - ansible_check_mode or result_authselect_check_cmd.rc == 0
          fail_msg:
          - authselect integrity check failed. Remediation aborted!
          - This remediation could not be applied because an authselect profile was
            not selected or the selected profile is not intact.
          - It is not recommended to manually edit the PAM files when authselect tool
            is available.
          - In cases where the default authselect profile does not cover a specific
            demand, a custom authselect profile is recommended.
          success_msg:
          - authselect integrity check passed

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          profile
        ansible.builtin.shell:
          cmd: authselect current -r | awk '{ print $1 }'
        register: result_authselect_profile
        changed_when: false
        when:
        - result_authselect_check_cmd is success

      - name: Set Lockout Time for Failed Password Attempts - Define the current authselect
          profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Define the new authselect
          custom profile as a local fact
        ansible.builtin.set_fact:
          authselect_current_profile: '{{ result_authselect_profile.stdout }}'
          authselect_custom_profile: custom/hardening
        when:
        - result_authselect_profile is not skipped
        - result_authselect_profile.stdout is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Get authselect current
          features to also enable them in the custom profile
        ansible.builtin.shell:
          cmd: authselect current | tail -n+3 | awk '{ print $2 }'
        register: result_authselect_features
        changed_when: false
        check_mode: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Check if any custom
          profile with the same name was already created
        ansible.builtin.stat:
          path: /etc/authselect/{{ authselect_custom_profile }}
        register: result_authselect_custom_profile_present
        changed_when: false
        when:
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on the current profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b {{ authselect_current_profile
            }}
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is not match("^(custom/|local)")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Create an authselect
          custom profile based on sssd profile
        ansible.builtin.command:
          cmd: authselect create-profile hardening -b sssd
        when:
        - result_authselect_profile is not skipped
        - result_authselect_check_cmd is success
        - authselect_current_profile is match("local")
        - not result_authselect_custom_profile_present.stat.exists

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Ensure the authselect
          custom profile is selected
        ansible.builtin.command:
          cmd: authselect select {{ authselect_custom_profile }}
        register: result_pam_authselect_select_profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - authselect_current_profile is not match("custom/")
        - authselect_custom_profile is not match(authselect_current_profile)

      - name: Set Lockout Time for Failed Password Attempts - Restore the authselect
          features in the custom profile
        ansible.builtin.command:
          cmd: authselect enable-feature {{ item }}
        loop: '{{ result_authselect_features.stdout_lines }}'
        register: result_pam_authselect_restore_features
        when:
        - result_authselect_profile is not skipped
        - result_authselect_features is not skipped
        - result_pam_authselect_select_profile is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
          are applied
        ansible.builtin.command:
          cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
        when:
        - result_authselect_check_cmd is success
        - result_authselect_profile is not skipped
        - result_pam_authselect_restore_features is not skipped

      - name: Set Lockout Time for Failed Password Attempts - Change the PAM file
          to be edited according to the custom authselect profile
        ansible.builtin.set_fact:
          pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
            | basename }}
        when:
        - authselect_custom_profile is defined
      when:
      - result_authselect_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Define a fact for control
        already filtered in case filters are used
      ansible.builtin.set_fact:
        pam_module_control: ''

    - name: Set Lockout Time for Failed Password Attempts - Check if {{ pam_file_path
        }} file is present
      ansible.builtin.stat:
        path: '{{ pam_file_path }}'
      register: result_pam_file_present

    - name: Set Lockout Time for Failed Password Attempts - Ensure the "unlock_time"
        option from "pam_faillock.so" is not present in {{ pam_file_path }}
      ansible.builtin.replace:
        dest: '{{ pam_file_path }}'
        regexp: (.*auth.*pam_faillock.so.*)\bunlock_time\b=?[0-9a-zA-Z]*(.*)
        replace: \1\2
      register: result_pam_option_removal
      when: result_pam_file_present.stat.exists

    - name: Set Lockout Time for Failed Password Attempts - Ensure authselect changes
        are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present.stat.exists
      - result_pam_option_removal is changed
    when:
    - result_pam_file_present.stat.exists
  when:
  - '"pam" in ansible_facts.packages'
  - result_faillock_conf_check.stat.exists
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Lockout Time for Failed Password Attempts - Ensure the pam_faillock.so
    unlock_time parameter in PAM files
  block:

  - name: Set Lockout Time for Failed Password Attempts - Check if pam_faillock.so
      unlock_time parameter is already enabled in pam files
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: .*auth.*pam_faillock\.so (preauth|authfail).*unlock_time
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_faillock_unlock_time_parameter_is_present

  - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of
      pam_faillock.so preauth unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
      line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the inclusion of
      pam_faillock.so authfail unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
      line: \1required\3 unlock_time={{ var_accounts_passwords_pam_faillock_unlock_time
        }}
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found == 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value
      for pam_faillock.so preauth unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)(unlock_time)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found > 0

  - name: Set Lockout Time for Failed Password Attempts - Ensure the desired value
      for pam_faillock.so authfail unlock_time parameter in auth section
    ansible.builtin.lineinfile:
      path: '{{ item }}'
      backrefs: true
      regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)(unlock_time)=[0-9]+(.*)
      line: \1required\3\4={{ var_accounts_passwords_pam_faillock_unlock_time }}\5
      state: present
    loop:
    - /etc/pam.d/system-auth
    - /etc/pam.d/password-auth
    when:
    - result_pam_faillock_unlock_time_parameter_is_present.found > 0
  when:
  - '"pam" in ansible_facts.packages'
  - not result_faillock_conf_check.stat.exists
  tags:
  - CCE-80670-3
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020014
  - DISA-STIG-RHEL-08-020015
  - NIST-800-171-3.1.8
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-8.1.7
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.4
  - accounts_passwords_pam_faillock_unlock_time
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

no more that one pam_unix.so is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_system_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_system_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/system-auth1

no more that one pam_unix.so is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_password_pam_unix_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_password_pam_unix_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\s*auth\N+pam_unix\.so/etc/pam.d/password-auth1

One and only one occurrence is expected in auth section of system-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_system_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_system_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/system-auth1

One and only one occurrence is expected in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_system_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_system_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/system-auth1

One and only one occurrence is expected in auth section of password-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_password_pam_faillock_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_password_pam_faillock_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail/etc/pam.d/password-auth1

One and only one occurrence is expected in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_password_pam_faillock_account:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_password_pam_faillock_account:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so/etc/pam.d/password-auth1

Check the expected unlock_time value in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_system:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
0
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+)
/etc/pam.d/system-auth1

Check the expected unlock_time value in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_password:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
0
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+)
/etc/pam.d/password-auth1

Check the absence of unlock_time parameter in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_no_faillock_conf:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*unlock_time[\s]*=[\s]*([0-9]+)/etc/security/faillock.conf1

Check the absence of unlock_time parameter in system-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_no_pamd_system:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_system:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+)/etc/pam.d/system-auth1

Check the absence of unlock_time parameter in password-auth  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_no_pamd_password:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_pamd_password:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*unlock_time=([0-9]+)/etc/pam.d/password-auth1

Check the expected unlock_time value in /etc/security/faillock.conf  oval:ssg-test_accounts_passwords_pam_faillock_unlock_time_parameter_faillock_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_passwords_pam_faillock_unlock_time_parameter_faillock_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
0
^[\s]*unlock_time[\s]*=[\s]*([0-9]+)
/etc/security/faillock.conf1
Ensure PAM Enforces Password Requirements - Minimum Digit Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit mediumCCE-80653-9

Ensure PAM Enforces Password Requirements - Minimum Digit Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_dcredit:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80653-9

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000071-GPOS-00039
anssiR31
pcidss48.3.6, 8.3
stigidRHEL-08-020130
stigrefSV-230359r1017171_rule
Description
The pam_pwquality module's dcredit parameter controls requirements for usage of digits in a password. When set to a negative number, any password will be required to contain that many digits. When set to a positive number, pam_pwquality will grant +1 additional length credit for each digit. Modify the dcredit setting in /etc/security/pwquality.conf to require the use of a digit in passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring digits makes password guessing attacks more difficult by ensuring a larger search space.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_dcredit='-1'



if grep -sq dcredit /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/dcredit/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dcredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dcredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^dcredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^dcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80653-9"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80653-9
  - DISA-STIG-RHEL-08-020130
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_dcredit # promote to variable
  set_fact:
    var_password_pam_dcredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Digit Characters - Find
    pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80653-9
  - DISA-STIG-RHEL-08-020130
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Digit Characters - Ensure
    dcredit is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bdcredit\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80653-9
  - DISA-STIG-RHEL-08-020130
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Digit Characters - Ensure
    PAM variable dcredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*dcredit
    line: dcredit = {{ var_password_pam_dcredit }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80653-9
  - DISA-STIG-RHEL-08-020130
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_dcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_dcredit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_dcredit:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*dcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Wordsxccdf_org.ssgproject.content_rule_accounts_password_pam_dictcheck mediumCCE-86233-4

Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary Words

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_dictcheck
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_dictcheck:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86233-4

References:
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
os-srgSRG-OS-000480-GPOS-00225, SRG-OS-000072-GPOS-00040
cis4.4.3.2.6
stigidRHEL-08-020300
stigrefSV-230377r1017188_rule
Description
The pam_pwquality module's dictcheck check if passwords contains dictionary words. When dictcheck is set to 1 passwords will be checked for dictionary words.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Passwords with dictionary words may be more vulnerable to password-guessing attacks.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_dictcheck='1'



if grep -sq dictcheck /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/dictcheck/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dictcheck")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dictcheck"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^dictcheck\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^dictcheck\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-86233-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86233-4
  - DISA-STIG-RHEL-08-020300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_dictcheck
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_dictcheck # promote to variable
  set_fact:
    var_password_pam_dictcheck: !!str 1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary
    Words - Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-86233-4
  - DISA-STIG-RHEL-08-020300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_dictcheck
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary
    Words - Ensure dictcheck is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bdictcheck\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-86233-4
  - DISA-STIG-RHEL-08-020300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_dictcheck
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Prevent the Use of Dictionary
    Words - Ensure PAM variable dictcheck is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*dictcheck
    line: dictcheck = {{ var_password_pam_dictcheck }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-86233-4
  - DISA-STIG-RHEL-08-020300
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_dictcheck
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_dictcheck:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_dictcheck:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*dictcheck[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Different Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_difok mediumCCE-80654-7

Ensure PAM Enforces Password Requirements - Minimum Different Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_difok
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_difok:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80654-7

References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.1.1
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(b), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
os-srgSRG-OS-000072-GPOS-00040
cis4.4.3.2.1
stigidRHEL-08-020170
stigrefSV-230363r1017175_rule
Description
The pam_pwquality module's difok parameter sets the number of characters in a password that must not be present in and old password during a password change.

Modify the difok setting in /etc/security/pwquality.conf to equal 8 to require differing characters when changing passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute–force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Requiring a minimum number of different characters during password changes ensures that newly changed passwords should not resemble previously compromised ones. Note that passwords which are changed on compromised systems will still be compromised, however.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_difok='8'



if grep -sq difok /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/difok/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^difok")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_difok"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^difok\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^difok\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80654-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80654-7
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020170
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(b)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_difok
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_difok # promote to variable
  set_fact:
    var_password_pam_difok: !!str 8
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Different Characters -
    Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80654-7
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020170
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(b)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_difok
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Different Characters -
    Ensure difok is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bdifok\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80654-7
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020170
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(b)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_difok
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Different Characters -
    Ensure PAM variable difok is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*difok
    line: difok = {{ var_password_pam_difok }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80654-7
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020170
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(b)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_difok
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_difok:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_difok:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*difok[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Lowercase Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit mediumCCE-80655-4

Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_lcredit:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80655-4

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000070-GPOS-00038
anssiR31
pcidss48.3.6, 8.3
stigidRHEL-08-020120
stigrefSV-230358r1017170_rule
Description
The pam_pwquality module's lcredit parameter controls requirements for usage of lowercase letters in a password. When set to a negative number, any password will be required to contain that many lowercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each lowercase character. Modify the lcredit setting in /etc/security/pwquality.conf to require the use of a lowercase character in passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possble combinations that need to be tested before the password is compromised. Requiring a minimum number of lowercase characters makes password guessing attacks more difficult by ensuring a larger search space.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_lcredit='-1'



if grep -sq lcredit /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/lcredit/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^lcredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_lcredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^lcredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^lcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80655-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80655-4
  - DISA-STIG-RHEL-08-020120
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_lcredit # promote to variable
  set_fact:
    var_password_pam_lcredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters -
    Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80655-4
  - DISA-STIG-RHEL-08-020120
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters -
    Ensure lcredit is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\blcredit\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80655-4
  - DISA-STIG-RHEL-08-020120
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Lowercase Characters -
    Ensure PAM variable lcredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*lcredit
    line: lcredit = {{ var_password_pam_lcredit }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80655-4
  - DISA-STIG-RHEL-08-020120
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_lcredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_lcredit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_lcredit:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*lcredit[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Classxccdf_org.ssgproject.content_rule_accounts_password_pam_maxclassrepeat mediumCCE-81034-1

Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating Characters from Same Character Class

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_maxclassrepeat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_maxclassrepeat:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-81034-1

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
os-srgSRG-OS-000072-GPOS-00040, SRG-OS-000730-GPOS-00190
stigidRHEL-08-020140
stigrefSV-230360r1017172_rule
Description
The pam_pwquality module's maxclassrepeat parameter controls requirements for consecutive repeating characters from the same character class. When set to a positive number, it will reject passwords which contain more than that number of consecutive characters from the same character class. Modify the maxclassrepeat setting in /etc/security/pwquality.conf to equal 4 to prevent a run of (4 + 1) or more identical characters.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.
Password complexity is one factor of several that determines how long it takes to crack a password. The more complex a password, the greater the number of possible combinations that need to be tested before the password is compromised.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_maxclassrepeat='4'



if grep -sq maxclassrepeat /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/maxclassrepeat/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxclassrepeat")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxclassrepeat"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^maxclassrepeat\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^maxclassrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-81034-1"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81034-1
  - DISA-STIG-RHEL-08-020140
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxclassrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_maxclassrepeat # promote to variable
  set_fact:
    var_password_pam_maxclassrepeat: !!str 4
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating
    Characters from Same Character Class - Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-81034-1
  - DISA-STIG-RHEL-08-020140
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxclassrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating
    Characters from Same Character Class - Ensure maxclassrepeat is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bmaxclassrepeat\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-81034-1
  - DISA-STIG-RHEL-08-020140
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxclassrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Maximum Consecutive Repeating
    Characters from Same Character Class - Ensure PAM variable maxclassrepeat is set
    accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*maxclassrepeat
    line: maxclassrepeat = {{ var_password_pam_maxclassrepeat }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-81034-1
  - DISA-STIG-RHEL-08-020140
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxclassrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_maxclassrepeat:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_maxclassrepeat:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*maxclassrepeat[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Set Password Maximum Consecutive Repeating Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_maxrepeat mediumCCE-82066-2

Set Password Maximum Consecutive Repeating Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_maxrepeat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_maxrepeat:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82066-2

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
os-srgSRG-OS-000072-GPOS-00040
cis4.4.3.2.4
stigidRHEL-08-020150
stigrefSV-230361r1017173_rule
Description
The pam_pwquality module's maxrepeat parameter controls requirements for consecutive repeating characters. When set to a positive number, it will reject passwords which contain more than that number of consecutive characters. Modify the maxrepeat setting in /etc/security/pwquality.conf to equal 3 to prevent a run of (3 + 1) or more identical characters.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Passwords with excessive repeating characters may be more vulnerable to password-guessing attacks.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_maxrepeat='3'



if grep -sq maxrepeat /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/maxrepeat/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^maxrepeat")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_maxrepeat"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^maxrepeat\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^maxrepeat\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-82066-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82066-2
  - DISA-STIG-RHEL-08-020150
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_maxrepeat # promote to variable
  set_fact:
    var_password_pam_maxrepeat: !!str 3
  tags:
    - always

- name: Set Password Maximum Consecutive Repeating Characters - Find pwquality.conf.d
    files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82066-2
  - DISA-STIG-RHEL-08-020150
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Password Maximum Consecutive Repeating Characters - Ensure maxrepeat is
    not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bmaxrepeat\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82066-2
  - DISA-STIG-RHEL-08-020150
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set Password Maximum Consecutive Repeating Characters - Ensure PAM variable
    maxrepeat is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*maxrepeat
    line: maxrepeat = {{ var_password_pam_maxrepeat }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82066-2
  - DISA-STIG-RHEL-08-020150
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_maxrepeat
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_maxrepeat:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_maxrepeat:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*maxrepeat[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Different Categoriesxccdf_org.ssgproject.content_rule_accounts_password_pam_minclass mediumCCE-82046-4

Ensure PAM Enforces Password Requirements - Minimum Different Categories

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_minclass
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_minclass:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-82046-4

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
os-srgSRG-OS-000072-GPOS-00040
anssiR68
cis4.4.3.2.3
stigidRHEL-08-020160
stigrefSV-230362r1017174_rule
Description
The pam_pwquality module's minclass parameter controls requirements for usage of different character classes, or types, of character that must exist in a password before it is considered valid. For example, setting this value to three (3) requires that any password must have characters from at least three different categories in order to be approved. The default value is zero (0), meaning there are no required classes. There are four categories available:
* Upper-case characters
* Lower-case characters
* Digits
* Special characters (for example, punctuation)
Modify the minclass setting in /etc/security/pwquality.conf entry to require 4 differing categories of characters when changing passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Requiring a minimum number of character categories makes password guessing attacks more difficult by ensuring a larger search space.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_minclass='4'



if grep -sq minclass /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/minclass/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minclass")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minclass"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^minclass\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^minclass\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-82046-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82046-4
  - DISA-STIG-RHEL-08-020160
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_minclass
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_minclass # promote to variable
  set_fact:
    var_password_pam_minclass: !!str 4
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Different Categories -
    Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82046-4
  - DISA-STIG-RHEL-08-020160
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_minclass
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Different Categories -
    Ensure minclass is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bminclass\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82046-4
  - DISA-STIG-RHEL-08-020160
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_minclass
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Different Categories -
    Ensure PAM variable minclass is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*minclass
    line: minclass = {{ var_password_pam_minclass }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-82046-4
  - DISA-STIG-RHEL-08-020160
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_minclass
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_minclass:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_minclass:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*minclass[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Lengthxccdf_org.ssgproject.content_rule_accounts_password_pam_minlen mediumCCE-80656-2

Ensure PAM Enforces Password Requirements - Minimum Length

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_minlen
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_minlen:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80656-2

References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.1.1
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000078-GPOS-00046
anssiR31, R68
cis4.4.3.2.2
pcidss48.3.6, 8.3
stigidRHEL-08-020230
stigrefSV-230369r1017181_rule
Description
The pam_pwquality module's minlen parameter controls requirements for minimum characters required in a password. Add minlen=15 after pam_pwquality to set minimum password length requirements.
Rationale
The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.
Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. Password length is one factor of several that helps to determine strength and how long it takes to crack a password. Use of more characters in a password helps to exponentially increase the time and/or resources required to compromise the password.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_minlen='15'



if grep -sq minlen /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/minlen/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minlen")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minlen"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^minlen\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^minlen\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80656-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80656-2
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020230
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_minlen # promote to variable
  set_fact:
    var_password_pam_minlen: !!str 15
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Length - Find pwquality.conf.d
    files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80656-2
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020230
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Length - Ensure minlen
    is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bminlen\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80656-2
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020230
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Length - Ensure PAM variable
    minlen is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*minlen
    line: minlen = {{ var_password_pam_minlen }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80656-2
  - CJIS-5.6.2.1.1
  - DISA-STIG-RHEL-08-020230
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.6
  - accounts_password_pam_minlen
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_minlen:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_minlen:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*minlen[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Special Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_ocredit mediumCCE-80663-8

Ensure PAM Enforces Password Requirements - Minimum Special Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_ocredit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_ocredit:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80663-8

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
os-srgSRG-OS-000266-GPOS-00101
anssiR31
stigidRHEL-08-020280
stigrefSV-230375r1017187_rule
Description
The pam_pwquality module's ocredit= parameter controls requirements for usage of special (or "other") characters in a password. When set to a negative number, any password will be required to contain that many special characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each special character. Modify the ocredit setting in /etc/security/pwquality.conf to equal -1 to require use of a special character in passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. Requiring a minimum number of special characters makes password guessing attacks more difficult by ensuring a larger search space.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_ocredit='-1'



if grep -sq ocredit /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/ocredit/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ocredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ocredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^ocredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^ocredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80663-8"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80663-8
  - DISA-STIG-RHEL-08-020280
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_ocredit # promote to variable
  set_fact:
    var_password_pam_ocredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Special Characters - Find
    pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80663-8
  - DISA-STIG-RHEL-08-020280
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Special Characters - Ensure
    ocredit is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bocredit\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80663-8
  - DISA-STIG-RHEL-08-020280
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Special Characters - Ensure
    PAM variable ocredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ocredit
    line: ocredit = {{ var_password_pam_ocredit }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80663-8
  - DISA-STIG-RHEL-08-020280
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - accounts_password_pam_ocredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_ocredit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_ocredit:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*ocredit[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Ensure PAM password complexity module is enabled in password-authxccdf_org.ssgproject.content_rule_accounts_password_pam_pwquality_password_auth mediumCCE-85877-9

Ensure PAM password complexity module is enabled in password-auth

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_pwquality_password_auth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_pwquality_password_auth:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85877-9

References:
os-srgSRG-OS-000069-GPOS-00037, SRG-OS-000070-GPOS-00038, SRG-OS-000480-GPOS-00227
stigidRHEL-08-020100
stigrefSV-230356r982195_rule
Description
To enable PAM password complexity in password-auth file: Edit the password section in /etc/pam.d/password-auth to show password requisite pam_pwquality.so.
Rationale
Enabling PAM password complexity permits to enforce strong passwords and consequently makes the system less prone to dictionary attacks.
OVAL test results details

check the configuration of /etc/pam.d/password-auth  oval:ssg-test_accounts_password_pam_pwquality_password_auth:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/password-authpassword requisite pam_pwquality.so
Ensure PAM password complexity module is enabled in system-authxccdf_org.ssgproject.content_rule_accounts_password_pam_pwquality_system_auth mediumCCE-85872-0

Ensure PAM password complexity module is enabled in system-auth

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_pwquality_system_auth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_pwquality_system_auth:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85872-0

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-020101
stigrefSV-251713r1017366_rule
Description
To enable PAM password complexity in system-auth file: Edit the password section in /etc/pam.d/system-auth to show password requisite pam_pwquality.so.
Rationale
Enabling PAM password complexity permits to enforce strong passwords and consequently makes the system less prone to dictionary attacks.
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_accounts_password_pam_pwquality_system_auth:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-authpassword requisite pam_pwquality.so
Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Sessionxccdf_org.ssgproject.content_rule_accounts_password_pam_retry mediumCCE-80664-6

Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted Per-Session

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_retry
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_retry:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80664-6

References:
cis-csc1, 11, 12, 15, 16, 3, 5, 9
cjis5.5.3
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), AC-7(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7, PR.IP-1
os-srgSRG-OS-000069-GPOS-00037, SRG-OS-000480-GPOS-00227
anssiR68
stigidRHEL-08-020104
stigrefSV-251716r1069329_rule
Description
To configure the number of retry prompts that are permitted per-session: Edit the /etc/security/pwquality.conf to include retry=3, or a lower value if site policy is more restrictive. The profile requirement is a maximum of retry=3 prompts per session.
Rationale
Setting the password retry prompts that are permitted on a per-session basis to a low value requires some software, such as SSH, to re-connect. This can slow down and draw additional attention to some types of password-guessing attacks. Note that this is different from account lockout, which is provided by the pam_faillock module.

# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_retry='3'


# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^retry")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_retry"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^retry\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^retry\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80664-6"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi
	
		if [ -e "/etc/pam.d/password-auth" ] ; then
    PAM_FILE_PATH="/etc/pam.d/password-auth"
    if [ -f /usr/bin/authselect ]; then
        
        if ! authselect check; then
        echo "
        authselect integrity check failed. Remediation aborted!
        This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
        It is not recommended to manually edit the PAM files when authselect tool is available.
        In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
        exit 1
        fi

        CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
        # If not already in use, a custom profile is created preserving the enabled features.
        if [[ ! $CURRENT_PROFILE == custom/* ]]; then
            ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
            # The "local" profile does not contain essential security features required by multiple Benchmarks.
            # If currently used, it is replaced by "sssd", which is the best option in this case.
            if [[ $CURRENT_PROFILE == local ]]; then
                CURRENT_PROFILE="sssd"
            fi
            authselect create-profile hardening -b $CURRENT_PROFILE
            CURRENT_PROFILE="custom/hardening"
            
            authselect apply-changes -b --backup=before-hardening-custom-profile
            authselect select $CURRENT_PROFILE
            for feature in $ENABLED_FEATURES; do
                authselect enable-feature $feature;
            done
            
            authselect apply-changes -b --backup=after-hardening-custom-profile
        fi
        PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth")
        PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

        authselect apply-changes -b
    fi
    
if grep -qP "^\s*password\s.*\bpam_pwquality.so\s.*\bretry\b" "$PAM_FILE_PATH"; then
    sed -i -E --follow-symlinks "s/(.*password.*pam_pwquality.so.*)\bretry\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
fi
    if [ -f /usr/bin/authselect ]; then
        
        authselect apply-changes -b
    fi
else
    echo "/etc/pam.d/password-auth was not found" >&2
fi
	
		if [ -e "/etc/pam.d/system-auth" ] ; then
    PAM_FILE_PATH="/etc/pam.d/system-auth"
    if [ -f /usr/bin/authselect ]; then
        
        if ! authselect check; then
        echo "
        authselect integrity check failed. Remediation aborted!
        This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
        It is not recommended to manually edit the PAM files when authselect tool is available.
        In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
        exit 1
        fi

        CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
        # If not already in use, a custom profile is created preserving the enabled features.
        if [[ ! $CURRENT_PROFILE == custom/* ]]; then
            ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
            # The "local" profile does not contain essential security features required by multiple Benchmarks.
            # If currently used, it is replaced by "sssd", which is the best option in this case.
            if [[ $CURRENT_PROFILE == local ]]; then
                CURRENT_PROFILE="sssd"
            fi
            authselect create-profile hardening -b $CURRENT_PROFILE
            CURRENT_PROFILE="custom/hardening"
            
            authselect apply-changes -b --backup=before-hardening-custom-profile
            authselect select $CURRENT_PROFILE
            for feature in $ENABLED_FEATURES; do
                authselect enable-feature $feature;
            done
            
            authselect apply-changes -b --backup=after-hardening-custom-profile
        fi
        PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth")
        PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"

        authselect apply-changes -b
    fi
    
if grep -qP "^\s*password\s.*\bpam_pwquality.so\s.*\bretry\b" "$PAM_FILE_PATH"; then
    sed -i -E --follow-symlinks "s/(.*password.*pam_pwquality.so.*)\bretry\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH"
fi
    if [ -f /usr/bin/authselect ]; then
        
        authselect apply-changes -b
    fi
else
    echo "/etc/pam.d/system-auth was not found" >&2
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
- name: XCCDF Value var_password_pam_retry # promote to variable
  set_fact:
    var_password_pam_retry: !!str 3
  tags:
    - always

- name: Ensure PAM variable retry is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^\s*retry
    line: retry = {{ var_password_pam_retry }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check if /etc/pam.d/password-auth file is present
  ansible.builtin.stat:
    path: /etc/pam.d/password-auth
  register: result_pam_file_present
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check the proper remediation for the system
  block:

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Define the PAM file to be edited as a local fact
    ansible.builtin.set_fact:
      pam_file_path: /etc/pam.d/password-auth

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Check if system relies on authselect tool
    ansible.builtin.stat:
      path: /usr/bin/authselect
    register: result_authselect_present

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure authselect custom profile is used if authselect
      is present
    block:

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Check integrity of authselect current profile
      ansible.builtin.command:
        cmd: authselect check
      register: result_authselect_check_cmd
      changed_when: false
      check_mode: false
      failed_when: false

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Informative message based on the authselect integrity
        check result
      ansible.builtin.assert:
        that:
        - ansible_check_mode or result_authselect_check_cmd.rc == 0
        fail_msg:
        - authselect integrity check failed. Remediation aborted!
        - This remediation could not be applied because an authselect profile was
          not selected or the selected profile is not intact.
        - It is not recommended to manually edit the PAM files when authselect tool
          is available.
        - In cases where the default authselect profile does not cover a specific
          demand, a custom authselect profile is recommended.
        success_msg:
        - authselect integrity check passed

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Get authselect current profile
      ansible.builtin.shell:
        cmd: authselect current -r | awk '{ print $1 }'
      register: result_authselect_profile
      changed_when: false
      when:
      - result_authselect_check_cmd is success

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Define the current authselect profile as a local fact
      ansible.builtin.set_fact:
        authselect_current_profile: '{{ result_authselect_profile.stdout }}'
        authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
      when:
      - result_authselect_profile is not skipped
      - result_authselect_profile.stdout is match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Define the new authselect custom profile as a local
        fact
      ansible.builtin.set_fact:
        authselect_current_profile: '{{ result_authselect_profile.stdout }}'
        authselect_custom_profile: custom/hardening
      when:
      - result_authselect_profile is not skipped
      - result_authselect_profile.stdout is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Get authselect current features to also enable them
        in the custom profile
      ansible.builtin.shell:
        cmd: authselect current | tail -n+3 | awk '{ print $2 }'
      register: result_authselect_features
      changed_when: false
      check_mode: false
      when:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Check if any custom profile with the same name was
        already created
      ansible.builtin.stat:
        path: /etc/authselect/{{ authselect_custom_profile }}
      register: result_authselect_custom_profile_present
      changed_when: false
      when:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Create an authselect custom profile based on the current
        profile
      ansible.builtin.command:
        cmd: authselect create-profile hardening -b {{ authselect_current_profile
          }}
      when:
      - result_authselect_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is not match("^(custom/|local)")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Create an authselect custom profile based on sssd
        profile
      ansible.builtin.command:
        cmd: authselect create-profile hardening -b sssd
      when:
      - result_authselect_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is match("local")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")
      - authselect_custom_profile is not match(authselect_current_profile)

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure the authselect custom profile is selected
      ansible.builtin.command:
        cmd: authselect select {{ authselect_custom_profile }}
      register: result_pam_authselect_select_profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")
      - authselect_custom_profile is not match(authselect_current_profile)

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Restore the authselect features in the custom profile
      ansible.builtin.command:
        cmd: authselect enable-feature {{ item }}
      loop: '{{ result_authselect_features.stdout_lines }}'
      register: result_pam_authselect_restore_features
      when:
      - result_authselect_profile is not skipped
      - result_authselect_features is not skipped
      - result_pam_authselect_select_profile is not skipped

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - result_pam_authselect_restore_features is not skipped

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Change the PAM file to be edited according to the
        custom authselect profile
      ansible.builtin.set_fact:
        pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
          | basename }}
      when:
      - authselect_custom_profile is defined
    when:
    - result_authselect_present.stat.exists

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Define a fact for control already filtered in case filters
      are used
    ansible.builtin.set_fact:
      pam_module_control: ''

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Check if {{ pam_file_path }} file is present
    ansible.builtin.stat:
      path: '{{ pam_file_path }}'
    register: result_pam_file_present

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is
      not present in {{ pam_file_path }}
    ansible.builtin.replace:
      dest: '{{ pam_file_path }}'
      regexp: (.*password.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*)
      replace: \1\2
    register: result_pam_option_removal
    when: result_pam_file_present.stat.exists

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_present.stat.exists
    - result_pam_option_removal is changed
  when:
  - '"libpwquality" in ansible_facts.packages'
  - result_pam_file_present.stat.exists
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check if /etc/pam.d/system-auth file is present
  ansible.builtin.stat:
    path: /etc/pam.d/system-auth
  register: result_pam_file_present
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
    Per-Session - Check the proper remediation for the system
  block:

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Define the PAM file to be edited as a local fact
    ansible.builtin.set_fact:
      pam_file_path: /etc/pam.d/system-auth

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Check if system relies on authselect tool
    ansible.builtin.stat:
      path: /usr/bin/authselect
    register: result_authselect_present

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure authselect custom profile is used if authselect
      is present
    block:

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Check integrity of authselect current profile
      ansible.builtin.command:
        cmd: authselect check
      register: result_authselect_check_cmd
      changed_when: false
      check_mode: false
      failed_when: false

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Informative message based on the authselect integrity
        check result
      ansible.builtin.assert:
        that:
        - ansible_check_mode or result_authselect_check_cmd.rc == 0
        fail_msg:
        - authselect integrity check failed. Remediation aborted!
        - This remediation could not be applied because an authselect profile was
          not selected or the selected profile is not intact.
        - It is not recommended to manually edit the PAM files when authselect tool
          is available.
        - In cases where the default authselect profile does not cover a specific
          demand, a custom authselect profile is recommended.
        success_msg:
        - authselect integrity check passed

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Get authselect current profile
      ansible.builtin.shell:
        cmd: authselect current -r | awk '{ print $1 }'
      register: result_authselect_profile
      changed_when: false
      when:
      - result_authselect_check_cmd is success

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Define the current authselect profile as a local fact
      ansible.builtin.set_fact:
        authselect_current_profile: '{{ result_authselect_profile.stdout }}'
        authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
      when:
      - result_authselect_profile is not skipped
      - result_authselect_profile.stdout is match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Define the new authselect custom profile as a local
        fact
      ansible.builtin.set_fact:
        authselect_current_profile: '{{ result_authselect_profile.stdout }}'
        authselect_custom_profile: custom/hardening
      when:
      - result_authselect_profile is not skipped
      - result_authselect_profile.stdout is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Get authselect current features to also enable them
        in the custom profile
      ansible.builtin.shell:
        cmd: authselect current | tail -n+3 | awk '{ print $2 }'
      register: result_authselect_features
      changed_when: false
      check_mode: false
      when:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Check if any custom profile with the same name was
        already created
      ansible.builtin.stat:
        path: /etc/authselect/{{ authselect_custom_profile }}
      register: result_authselect_custom_profile_present
      changed_when: false
      when:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Create an authselect custom profile based on the current
        profile
      ansible.builtin.command:
        cmd: authselect create-profile hardening -b {{ authselect_current_profile
          }}
      when:
      - result_authselect_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is not match("^(custom/|local)")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Create an authselect custom profile based on sssd
        profile
      ansible.builtin.command:
        cmd: authselect create-profile hardening -b sssd
      when:
      - result_authselect_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is match("local")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")
      - authselect_custom_profile is not match(authselect_current_profile)

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure the authselect custom profile is selected
      ansible.builtin.command:
        cmd: authselect select {{ authselect_custom_profile }}
      register: result_pam_authselect_select_profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")
      - authselect_custom_profile is not match(authselect_current_profile)

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Restore the authselect features in the custom profile
      ansible.builtin.command:
        cmd: authselect enable-feature {{ item }}
      loop: '{{ result_authselect_features.stdout_lines }}'
      register: result_pam_authselect_restore_features
      when:
      - result_authselect_profile is not skipped
      - result_authselect_features is not skipped
      - result_pam_authselect_select_profile is not skipped

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
      when:
      - result_authselect_check_cmd is success
      - result_authselect_profile is not skipped
      - result_pam_authselect_restore_features is not skipped

    - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
        Permitted Per-Session - Change the PAM file to be edited according to the
        custom authselect profile
      ansible.builtin.set_fact:
        pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
          | basename }}
      when:
      - authselect_custom_profile is defined
    when:
    - result_authselect_present.stat.exists

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Define a fact for control already filtered in case filters
      are used
    ansible.builtin.set_fact:
      pam_module_control: ''

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Check if {{ pam_file_path }} file is present
    ansible.builtin.stat:
      path: '{{ pam_file_path }}'
    register: result_pam_file_present

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure the "retry" option from "pam_pwquality.so" is
      not present in {{ pam_file_path }}
    ansible.builtin.replace:
      dest: '{{ pam_file_path }}'
      regexp: (.*password.*pam_pwquality.so.*)\bretry\b=?[0-9a-zA-Z]*(.*)
      replace: \1\2
    register: result_pam_option_removal
    when: result_pam_file_present.stat.exists

  - name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
      Permitted Per-Session - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_present.stat.exists
    - result_pam_option_removal is changed
  when:
  - '"libpwquality" in ansible_facts.packages'
  - result_pam_file_present.stat.exists
  tags:
  - CCE-80664-6
  - CJIS-5.5.3
  - DISA-STIG-RHEL-08-020104
  - NIST-800-53-AC-7(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(4)
  - accounts_password_pam_retry
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
OVAL test results details

The 'fingerprint-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_fingerprint_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/fingerprint-auth/etc/authselect/fingerprint-auth

The 'password-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_password_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/password-auth/etc/authselect/password-auth

The 'postlogin' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_postlogin_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/postlogin/etc/authselect/postlogin

The 'smartcard-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_smartcard_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/smartcard-auth/etc/authselect/smartcard-auth

The 'system-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_system_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/system-auth/etc/authselect/system-auth

check the configuration of /etc/pam.d/password-auth  oval:ssg-test_password_pam_pwquality_retry_password_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_retry_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/password-auth^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$1

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality_retry_system_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_retry_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/system-auth^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$1

check the configuration of /etc/pam.d/password-auth  oval:ssg-test_password_pam_pwquality_retry_password_auth_not_set:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_retry_password_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/password-auth^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$1

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality_retry_system_auth_not_set:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_retry_system_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/system-auth^\s*password\s+(?:(?:required)|(?:requisite))\s+pam_pwquality\.so.*retry=([0-9]*).*$1

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_retry_pwquality_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_retry_pwquality_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/pwquality.conf^[\s]*retry[\s]*=[\s]*(\d+)(?:[\s]|$)1
Ensure PAM Enforces Password Requirements - Minimum Uppercase Charactersxccdf_org.ssgproject.content_rule_accounts_password_pam_ucredit mediumCCE-80665-3

Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_pam_ucredit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_pam_ucredit:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80665-3

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(a), CM-6(a), IA-5(4)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
osppFMT_SMF_EXT.1
pcidssReq-8.2.3
os-srgSRG-OS-000069-GPOS-00037, SRG-OS-000070-GPOS-00038
anssiR31
stigidRHEL-08-020110
stigrefSV-230357r1017169_rule
Description
The pam_pwquality module's ucredit= parameter controls requirements for usage of uppercase letters in a password. When set to a negative number, any password will be required to contain that many uppercase characters. When set to a positive number, pam_pwquality will grant +1 additional length credit for each uppercase character. Modify the ucredit setting in /etc/security/pwquality.conf to require the use of an uppercase character in passwords.
Rationale
Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks.

Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q libpwquality; then

var_password_pam_ucredit='-1'



if grep -sq ucredit /etc/security/pwquality.conf.d/*.conf ; then
    sed -i "/ucredit/d" /etc/security/pwquality.conf.d/*.conf
fi






# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ucredit")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_ucredit"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^ucredit\\>" "/etc/security/pwquality.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^ucredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf"
else
    if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf"
    fi
    cce="CCE-80665-3"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf"
    printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80665-3
  - DISA-STIG-RHEL-08-020110
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_password_pam_ucredit # promote to variable
  set_fact:
    var_password_pam_ucredit: !!str -1
  tags:
    - always

- name: Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters -
    Find pwquality.conf.d files
  ansible.builtin.find:
    paths: /etc/security/pwquality.conf.d/
    patterns: '*.conf'
  register: pwquality_conf_d_files
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80665-3
  - DISA-STIG-RHEL-08-020110
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters -
    Ensure ucredit is not set in pwquality.conf.d
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^\s*\bucredit\b.*
    state: absent
  with_items: '{{ pwquality_conf_d_files.files }}'
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80665-3
  - DISA-STIG-RHEL-08-020110
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure PAM Enforces Password Requirements - Minimum Uppercase Characters -
    Ensure PAM variable ucredit is set accordingly
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/security/pwquality.conf
    regexp: ^#?\s*ucredit
    line: ucredit = {{ var_password_pam_ucredit }}
  when: '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-80665-3
  - DISA-STIG-RHEL-08-020110
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(4)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - accounts_password_pam_ucredit
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

check the configuration of /etc/pam.d/system-auth  oval:ssg-test_password_pam_pwquality:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth password requisite pam_pwquality.so local_users_only

check the configuration of /etc/security/pwquality.conf  oval:ssg-test_password_pam_pwquality_ucredit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_password_pam_pwquality_ucredit:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/security/pwquality\.conf(\.d/[^/]+\.conf)?$^\s*ucredit[\s]*=[\s]*(-?\d+)(?:[\s]|$)1
Set Password Hashing Algorithm in /etc/login.defsxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs mediumCCE-80892-3

Set Password Hashing Algorithm in /etc/login.defs

Rule IDxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-set_password_hashing_algorithm_logindefs:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80892-3

References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.2
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.13.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0418, 1055, 1402
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(c), CM-6(a)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
pcidssReq-8.2.1
os-srgSRG-OS-000073-GPOS-00041
cis4.5.1.1
pcidss48.3.2, 8.3
stigidRHEL-08-010110
stigrefSV-230231r1017050_rule
Description
In /etc/login.defs, add or update the following line to ensure the system will use SHA512 as the hashing algorithm:
ENCRYPT_METHOD SHA512
Rationale
Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are encrypted with a weak algorithm are no more protected than if they are kept in plain text.

Using a stronger hashing algorithm makes password cracking attacks more difficult.
OVAL test results details

The value of ENCRYPT_METHOD should be set appropriately in /etc/login.defs  oval:ssg-test_set_password_hashing_algorithm_logindefs:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-variable_last_encrypt_method_instance_value:var:1SHA512
Set PAM''s Password Hashing Algorithm - password-authxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_passwordauth mediumCCE-85945-4

Set PAM''s Password Hashing Algorithm - password-auth

Rule IDxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_passwordauth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-set_password_hashing_algorithm_passwordauth:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-85945-4

References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.2
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.13.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0418, 1055, 1402
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(c), CM-6(a)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
pcidssReq-8.2.1
os-srgSRG-OS-000073-GPOS-00041, SRG-OS-000120-GPOS-00061
cis4.4.3.4.3
stigidRHEL-08-010160
stigrefSV-230237r1017056_rule
Description
The PAM system service can be configured to only store encrypted representations of passwords. In /etc/pam.d/password-auth, the password section of the file controls which PAM modules to execute during a password change. Set the pam_unix.so module in the password section to include the option sha512 and no other hashing algorithms as shown below:
password    sufficient    pam_unix.so sha512 other arguments...

This will help ensure that new passwords for local users will be stored using the sha512 algorithm.
Rationale
Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are encrypted with a weak algorithm are no more protected than if they are kept in plain text.

This setting ensures user and group account administration utilities are configured to store only encrypted representations of passwords. Additionally, the crypt_style configuration option in /etc/libuser.conf ensures the use of a strong hashing algorithm that makes password cracking attacks more difficult.
Warnings
warning  The hashing algorithms to be used with pam_unix.so are defined with independent module options. There are at least 7 possible algorithms and likely more algorithms will be introduced along the time. Due the the number of options and its possible combinations, the use of multiple hashing algorithm options may bring unexpected behaviors to the system. For this reason the check will pass only when one hashing algorithm option is defined and is aligned to the "var_password_hashing_algorithm_pam" variable. The remediation will ensure the correct option and remove any other extra hashing algorithm option.
OVAL test results details

check if pam_unix.so hashing algorithm option is correct and specified only once in /etc/pam.d/password-auth  oval:ssg-test_set_password_hashing_algorithm_passwordauth:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/pam.d/password-authpassword sufficient pam_unix.so sha512 shadow nullok use_authtok
Set PAM''s Password Hashing Algorithmxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth mediumCCE-80893-1

Set PAM''s Password Hashing Algorithm

Rule IDxccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-set_password_hashing_algorithm_systemauth:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-80893-1

References:
cis-csc1, 12, 15, 16, 5
cjis5.6.2.2
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.13.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
ism0418, 1055, 1402
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistIA-5(c), IA-5(1)(c), CM-6(a)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
pcidssReq-8.2.1
os-srgSRG-OS-000073-GPOS-00041, SRG-OS-000120-GPOS-00061
anssiR68
cis4.4.3.4.3
pcidss48.3.2, 8.3
stigidRHEL-08-010159
stigrefSV-244524r1017330_rule
Description
The PAM system service can be configured to only store encrypted representations of passwords. In "/etc/pam.d/system-auth", the password section of the file controls which PAM modules to execute during a password change. Set the pam_unix.so module in the password section to include the option sha512 and no other hashing algorithms as shown below:
password    sufficient    pam_unix.so sha512 other arguments...

This will help ensure that new passwords for local users will be stored using the sha512 algorithm.
Rationale
Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are encrypted with a weak algorithm are no more protected than if they are kept in plain text.

This setting ensures user and group account administration utilities are configured to store only encrypted representations of passwords. Additionally, the crypt_style configuration option in /etc/libuser.conf ensures the use of a strong hashing algorithm that makes password cracking attacks more difficult.
Warnings
warning  The hashing algorithms to be used with pam_unix.so are defined with independent module options. There are at least 7 possible algorithms and likely more algorithms will be introduced along the time. Due the the number of options and its possible combinations, the use of multiple hashing algorithm options may bring unexpected behaviors to the system. For this reason the check will pass only when one hashing algorithm option is defined and is aligned to the "var_password_hashing_algorithm_pam" variable. The remediation will ensure the correct option and remove any other extra hashing algorithm option.
OVAL test results details

check if pam_unix.so hashing algorithm option is correct and specified only once in /etc/pam.d/system-auth  oval:ssg-test_pam_unix_hashing_algorithm_systemauth:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/pam.d/system-authpassword sufficient pam_unix.so sha512 shadow nullok use_authtok
Set Password Hashing Rounds in /etc/login.defsxccdf_org.ssgproject.content_rule_set_password_hashing_min_rounds_logindefs mediumCCE-89707-4

Set Password Hashing Rounds in /etc/login.defs

Rule IDxccdf_org.ssgproject.content_rule_set_password_hashing_min_rounds_logindefs
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-set_password_hashing_min_rounds_logindefs:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-89707-4

References:
os-srgSRG-OS-000073-GPOS-00041, SRG-OS-000120-GPOS-00061
stigidRHEL-08-010130
stigrefSV-230233r1044790_rule
Description
In /etc/login.defs, ensure SHA_CRYPT_MIN_ROUNDS and SHA_CRYPT_MAX_ROUNDS has the minimum value of 100000. For example:
SHA_CRYPT_MIN_ROUNDS 100000
SHA_CRYPT_MAX_ROUNDS 100000
Notice that if neither are set, they already have the default value of 5000. If either is set, they must have the minimum value of 100000.
Rationale
Passwords need to be protected at all times, and hashing is the standard method for protecting passwords. If passwords are not hashed, they can be plainly read (i.e., clear text) and easily compromised. Passwords that are hashed with a weak algorithm are no more protected than if they are kept in plain text.

Using more hashing rounds makes password cracking attacks more difficult.


var_password_hashing_min_rounds_login_defs='100000'


config_file=/etc/login.defs
current_min_rounds=$(grep -Po '^\s*SHA_CRYPT_MIN_ROUNDS\s+\K\d+' "$config_file")
current_max_rounds=$(grep -Po '^\s*SHA_CRYPT_MAX_ROUNDS\s+\K\d+' "$config_file")

if [[ -z "$current_min_rounds" || "$current_min_rounds" -le "$var_password_hashing_min_rounds_login_defs" ]]; then
    if [ -e "/etc/login.defs" ] ; then
        
        LC_ALL=C sed -i "/^\s*SHA_CRYPT_MIN_ROUNDS\s*/Id" "/etc/login.defs"
    else
        printf '%s\n' "Path '/etc/login.defs' wasn't found on this system. Refusing to continue." >&2
        return 1
    fi
    # make sure file has newline at the end
    sed -i -e '$a\' "/etc/login.defs"

    cp "/etc/login.defs" "/etc/login.defs.bak"
    # Insert at the end of the file
    printf '%s\n' "SHA_CRYPT_MIN_ROUNDS $var_password_hashing_min_rounds_login_defs" >> "/etc/login.defs"
    # Clean up after ourselves.
    rm "/etc/login.defs.bak"
fi

if [[ -n "$current_max_rounds" && "$current_max_rounds" -le "$var_password_hashing_min_rounds_login_defs" ]]; then
    if [ -e "/etc/login.defs" ] ; then
        
        LC_ALL=C sed -i "/^\s*SHA_CRYPT_MAX_ROUNDS\s*/Id" "/etc/login.defs"
    else
        printf '%s\n' "Path '/etc/login.defs' wasn't found on this system. Refusing to continue." >&2
        return 1
    fi
    # make sure file has newline at the end
    sed -i -e '$a\' "/etc/login.defs"

    cp "/etc/login.defs" "/etc/login.defs.bak"
    # Insert at the end of the file
    printf '%s\n' "SHA_CRYPT_MAX_ROUNDS $var_password_hashing_min_rounds_login_defs" >> "/etc/login.defs"
    # Clean up after ourselves.
    rm "/etc/login.defs.bak"
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_password_hashing_min_rounds_login_defs # promote to variable
  set_fact:
    var_password_hashing_min_rounds_login_defs: !!str 100000
  tags:
    - always

- name: Set Password Hashing Rounds in /etc/login.defs - extract contents of the file
    /etc/login.defs
  ansible.builtin.slurp:
    src: /etc/login.defs
  register: etc_login_defs
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - extract the value of SHA_CRYPT_MIN_ROUNDS
    if present
  ansible.builtin.set_fact:
    etc_login_defs_sha_crypt_min_rounds: '{{ etc_login_defs[''content''] | b64decode
      | regex_search(''^\s*SHA_CRYPT_MIN_ROUNDS\s+(\d+)'', ''\1'', multiline=True)
      | default([], true) }}'
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - extract the value of SHA_CRYPT_MAX_ROUNDS
    if present
  ansible.builtin.set_fact:
    etc_login_defs_sha_crypt_max_rounds: '{{ etc_login_defs[''content''] | b64decode
      | regex_search(''^\s*SHA_CRYPT_MAX_ROUNDS\s+(\d+)'', ''\1'', multiline=True)
      | default([], true) }}'
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - Ensure SHA_CRYPT_MIN_ROUNDS
    has Minimum Value of 5000
  ansible.builtin.replace:
    path: /etc/login.defs
    regexp: (^\s*SHA_CRYPT_MIN_ROUNDS\s+)(?:\d+)(.*$)
    replace: \g<1>{{ var_password_hashing_min_rounds_login_defs }}\g<2>
    backup: false
  when: etc_login_defs_sha_crypt_min_rounds is defined and etc_login_defs_sha_crypt_min_rounds
    | length > 0 and etc_login_defs_sha_crypt_min_rounds | first | int < var_password_hashing_min_rounds_login_defs
    | int
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - Ensure SHA_CRYPT_MAX_ROUNDS
    has Minimum Value of 5000
  ansible.builtin.replace:
    path: /etc/login.defs
    regexp: (^\s*SHA_CRYPT_MAX_ROUNDS\s+)(?:\d+)(.*$)
    replace: \g<1>{{ var_password_hashing_min_rounds_login_defs }}\g<2>
    backup: false
  when: etc_login_defs_sha_crypt_max_rounds is defined and etc_login_defs_sha_crypt_max_rounds
    | length > 0 and etc_login_defs_sha_crypt_max_rounds | first | int < var_password_hashing_min_rounds_login_defs
    | int
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - SHA_CRYPT_MIN_ROUNDS add
    configuration if not found
  ansible.builtin.lineinfile:
    line: SHA_CRYPT_MIN_ROUNDS {{ var_password_hashing_min_rounds_login_defs }}
    path: /etc/login.defs
    state: present
  when: etc_login_defs_sha_crypt_min_rounds | length == 0
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs

- name: Set Password Hashing Rounds in /etc/login.defs - SHA_CRYPT_MAX_ROUNDS add
    configuration if not found
  ansible.builtin.lineinfile:
    line: SHA_CRYPT_MAX_ROUNDS {{ var_password_hashing_min_rounds_login_defs }}
    path: /etc/login.defs
    state: present
  when: etc_login_defs_sha_crypt_max_rounds | length == 0
  tags:
  - CCE-89707-4
  - DISA-STIG-RHEL-08-010130
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - set_password_hashing_min_rounds_logindefs
OVAL test results details

SHA_CRYPT_MIN_ROUNDS is not explicitly configured in /etc/login.defs and therefore takes on the default value  oval:ssg-test_etc_login_defs_sha_crypt_min_rounds_default:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_etc_login_defs_sha_crypt_min_rounds_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/login.defs^\s*SHA_CRYPT_MIN_ROUNDS\s*1

Check if the variable is set to 5000 or lower  oval:ssg-test_var_password_hashing_min_rounds_login_defs_le_5000:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-local_var_password_hashing_min_rounds_login_defs:var:1100000

SHA_CRYPT_MIN_ROUNDS is explicitly configured in /etc/login.defs and its value most be greater or equal to 5000  oval:ssg-test_etc_login_defs_sha_crypt_min_rounds_present:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_etc_login_defs_sha_crypt_min_rounds_present:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/login.defs^\s*SHA_CRYPT_MIN_ROUNDS\s+(\d+)\s*$1

Check if the variable is set to 5000 or lower  oval:ssg-test_var_password_hashing_min_rounds_login_defs_le_5000:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-local_var_password_hashing_min_rounds_login_defs:var:1100000

SHA_CRYPT_MAX_ROUNDS is not explicitly configured in /etc/login.defs and therefore takes on the default value  oval:ssg-test_etc_login_defs_sha_crypt_max_rounds_default:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/login.defsSHA_CRYPT_MAX_ROUNDS

SHA_CRYPT_MIN_ROUNDS is not explicitly configured in /etc/login.defs and therefore takes on the default value  oval:ssg-test_etc_login_defs_sha_crypt_min_rounds_default:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_etc_login_defs_sha_crypt_min_rounds_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/login.defs^\s*SHA_CRYPT_MIN_ROUNDS\s*1

SHA_CRYPT_MAX_ROUNDS is explicitly configured in /etc/login.defs and its value most be greater or equal to 5000  oval:ssg-test_etc_login_defs_sha_crypt_max_rounds_present:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/login.defsSHA_CRYPT_MAX_ROUNDS 5000
Disallow Configuration to Bypass Password Requirements for Privilege Escalationxccdf_org.ssgproject.content_rule_disallow_bypass_password_sudo mediumCCE-86319-1

Disallow Configuration to Bypass Password Requirements for Privilege Escalation

Rule IDxccdf_org.ssgproject.content_rule_disallow_bypass_password_sudo
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-disallow_bypass_password_sudo:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-86319-1

References:
nistIA-11
os-srgSRG-OS-000373-GPOS-00156, SRG-OS-000373-GPOS-00157, SRG-OS-000373-GPOS-00158
stigidRHEL-08-010385
stigrefSV-251712r1050789_rule
Description
Verify the operating system is not configured to bypass password requirements for privilege escalation. Check the configuration of the "/etc/pam.d/sudo" file with the following command:
$ sudo grep pam_succeed_if /etc/pam.d/sudo
If any occurrences of "pam_succeed_if" is returned from the command, this is a finding.
Rationale
Without re-authentication, users may access resources or perform tasks for which they do not have authorization. When operating systems provide the capability to escalate a functional capability, it is critical the user re-authenticate.
OVAL test results details

Check absence of conf pam_succeed_if in /etc/pam.d/sudo  oval:ssg-test_disallow_bypass_password_sudo:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_disallow_bypass_password_sudo:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/sudo^.*pam_succeed_if.*$1
Install the opensc Package For Multifactor Authenticationxccdf_org.ssgproject.content_rule_package_opensc_installed mediumCCE-80846-9

Install the opensc Package For Multifactor Authentication

Rule IDxccdf_org.ssgproject.content_rule_package_opensc_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_opensc_installed:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-80846-9

References:
ism1382, 1384, 1386
nistCM-6(a)
os-srgSRG-OS-000375-GPOS-00160, SRG-OS-000376-GPOS-00161
stigidRHEL-08-010410
stigrefSV-230275r958816_rule
Description
The opensc package can be installed with the following command:
$ sudo yum install opensc
Rationale
Using an authentication device, such as a CAC or token that is separate from the information system, ensures that even if the information system is compromised, that compromise will not affect credentials stored on the authentication device.

Multifactor solutions that require devices separate from information systems gaining access include, for example, hardware tokens providing time-based or challenge-response authenticators and smart cards or similar secure authentication devices issued by an organization or identity provider.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "opensc" ; then
    yum install -y "opensc"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80846-9
  - DISA-STIG-RHEL-08-010410
  - NIST-800-53-CM-6(a)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_opensc_installed

- name: Ensure opensc is installed
  ansible.builtin.package:
    name: opensc
    state: present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80846-9
  - DISA-STIG-RHEL-08-010410
  - NIST-800-53-CM-6(a)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_opensc_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_opensc

class install_opensc {
  package { 'opensc':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=opensc


[[packages]]
name = "opensc"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install opensc

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install opensc
OVAL test results details

package opensc is installed  oval:ssg-test_package_opensc_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_opensc_installed:obj:1 of type rpminfo_object
Name
opensc
Install Smart Card Packages For Multifactor Authenticationxccdf_org.ssgproject.content_rule_install_smartcard_packages mediumCCE-84029-8

Install Smart Card Packages For Multifactor Authentication

Rule IDxccdf_org.ssgproject.content_rule_install_smartcard_packages
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-install_smartcard_packages:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-84029-8

References:
nistCM-6(a)
pcidssReq-8.3
os-srgSRG-OS-000105-GPOS-00052, SRG-OS-000375-GPOS-00160, SRG-OS-000375-GPOS-00161, SRG-OS-000377-GPOS-00162
stigidRHEL-08-010390
stigrefSV-230273r1017381_rule
Description
Configure the operating system to implement multifactor authentication by installing the required package with the following command: The openssl-pkcs11 package can be installed with the following command:
$ sudo yum install openssl-pkcs11
Rationale
Using an authentication device, such as a CAC or token that is separate from the information system, ensures that even if the information system is compromised, that compromise will not affect credentials stored on the authentication device.

Multifactor solutions that require devices separate from information systems gaining access include, for example, hardware tokens providing time-based or challenge-response authenticators and smart cards or similar secure authentication devices issued by an organization or identity provider.
OVAL test results details

package openssl-pkcs11 is installed  oval:ssg-test_package_openssl-pkcs11_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssl-pkcs11x86_64(none)3.el80.4.100:0.4.10-3.el8199e2f91fd431d51openssl-pkcs11-0:0.4.10-3.el8.x86_64
Disable debug-shell SystemD Servicexccdf_org.ssgproject.content_rule_service_debug-shell_disabled mediumCCE-80876-6

Disable debug-shell SystemD Service

Rule IDxccdf_org.ssgproject.content_rule_service_debug-shell_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_debug-shell_disabled:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-80876-6

References:
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
nistCM-6
osppFIA_UAU.1
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040180
stigrefSV-230532r1017294_rule
Description
SystemD's debug-shell service is intended to diagnose SystemD related boot issues with various systemctl commands. Once enabled and following a system reboot, the root shell will be available on tty9 which is access by pressing CTRL-ALT-F9. The debug-shell service should only be used for SystemD related issues and should otherwise be disabled.

By default, the debug-shell SystemD service is already disabled. The debug-shell service can be disabled with the following command:
$ sudo systemctl mask --now debug-shell.service
Rationale
This prevents attackers with physical access from trivially bypassing security on the machine through valid troubleshooting configurations and gaining root access when the system is rebooted.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
  "$SYSTEMCTL_EXEC" stop 'debug-shell.service'
fi
"$SYSTEMCTL_EXEC" disable 'debug-shell.service'
"$SYSTEMCTL_EXEC" mask 'debug-shell.service'
# Disable socket activation if we have a unit file for it
if "$SYSTEMCTL_EXEC" -q list-unit-files debug-shell.socket; then
    if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
      "$SYSTEMCTL_EXEC" stop 'debug-shell.socket'
    fi
    "$SYSTEMCTL_EXEC" mask 'debug-shell.socket'
fi
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
"$SYSTEMCTL_EXEC" reset-failed 'debug-shell.service' || true

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80876-6
  - DISA-STIG-RHEL-08-040180
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled

- name: Disable debug-shell SystemD Service - Disable service debug-shell
  block:

  - name: Disable debug-shell SystemD Service - Collect systemd Services Present in
      the System
    ansible.builtin.command: systemctl -q list-unit-files --type service
    register: service_exists
    changed_when: false
    failed_when: service_exists.rc not in [0, 1]
    check_mode: false

  - name: Disable debug-shell SystemD Service - Ensure debug-shell.service is Masked
    ansible.builtin.systemd:
      name: debug-shell.service
      state: stopped
      enabled: false
      masked: true
    when: service_exists.stdout_lines is search("debug-shell.service", multiline=True)

  - name: Unit Socket Exists - debug-shell.socket
    ansible.builtin.command: systemctl -q list-unit-files debug-shell.socket
    register: socket_file_exists
    changed_when: false
    failed_when: socket_file_exists.rc not in [0, 1]
    check_mode: false

  - name: Disable debug-shell SystemD Service - Disable Socket debug-shell
    ansible.builtin.systemd:
      name: debug-shell.socket
      enabled: false
      state: stopped
      masked: true
    when: socket_file_exists.stdout_lines is search("debug-shell.socket", multiline=True)
  tags:
  - CCE-80876-6
  - DISA-STIG-RHEL-08-040180
  - NIST-800-171-3.4.5
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_debug-shell_disabled
  - special_service_block
  when: '"kernel" in ansible_facts.packages'

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include disable_debug-shell

class disable_debug-shell {
  service {'debug-shell':
    enable => false,
    ensure => 'stopped',
  }
}

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: debug-shell.service
        enabled: false
        mask: true
      - name: debug-shell.socket
        enabled: false
        mask: true


[customizations.services]
masked = ["debug-shell"]

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

service disable debug-shell
OVAL test results details

package systemd is removed  oval:ssg-service_debug-shell_disabled_test_service_debug-shell_package_systemd_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedsystemdx86_64(none)82.el82390:239-82.el8199e2f91fd431d51systemd-0:239-82.el8.x86_64

Test that the debug-shell service is not running  oval:ssg-test_service_not_running_service_debug-shell_disabled_debug-shell:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
truedebug-shell.serviceActiveStateinactive

Test that the property LoadState from the service debug-shell is masked  oval:ssg-test_service_loadstate_is_masked_service_debug-shell_disabled_debug-shell:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsedebug-shell.serviceLoadStateloaded

Test that the service debug-shell is not found  oval:ssg-test_service_not_found_service_debug-shell_disabled_debug-shell:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsedebug-shell.serviceLoadStateloaded
Disable Ctrl-Alt-Del Burst Actionxccdf_org.ssgproject.content_rule_disable_ctrlaltdel_burstaction highCCE-80784-2

Disable Ctrl-Alt-Del Burst Action

Rule IDxccdf_org.ssgproject.content_rule_disable_ctrlaltdel_burstaction
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-disable_ctrlaltdel_burstaction:def:1
Time2025-11-18T17:05:04-05:00
Severityhigh
Identifiers:

CCE-80784-2

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1), CM-6(a)
nist-csfPR.AC-4, PR.DS-5
osppFAU_GEN.1.2
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040172
stigrefSV-230531r1017292_rule
Description
By default, SystemD will reboot the system if the Ctrl-Alt-Del key sequence is pressed Ctrl-Alt-Delete more than 7 times in 2 seconds.

To configure the system to ignore the CtrlAltDelBurstAction setting, add or modify the following to /etc/systemd/system.conf:
CtrlAltDelBurstAction=none
Rationale
A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.
Warnings
warning  Disabling the Ctrl-Alt-Del key sequence in /etc/init/control-alt-delete.conf DOES NOT disable the Ctrl-Alt-Del key sequence if running in runlevel 6 (e.g. in GNOME, KDE, etc.)! The Ctrl-Alt-Del key sequence will only be disabled if running in the non-graphical runlevel 3.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q systemd; }; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^CtrlAltDelBurstAction=")

# shellcheck disable=SC2059
printf -v formatted_output "%s=%s" "$stripped_key" "none"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^CtrlAltDelBurstAction=\\>" "/etc/systemd/system.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^CtrlAltDelBurstAction=\\>.*/$escaped_formatted_output/gi" "/etc/systemd/system.conf"
else
    if [[ -s "/etc/systemd/system.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/systemd/system.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/systemd/system.conf"
    fi
    cce="CCE-80784-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/systemd/system.conf" >> "/etc/systemd/system.conf"
    printf '%s\n' "$formatted_output" >> "/etc/systemd/system.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80784-2
  - DISA-STIG-RHEL-08-040172
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_burstaction
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

- name: Disable Ctrl-Alt-Del Burst Action
  ansible.builtin.lineinfile:
    dest: /etc/systemd/system.conf
    state: present
    regexp: ^CtrlAltDelBurstAction
    line: CtrlAltDelBurstAction=none
    create: true
  when:
  - '"kernel" in ansible_facts.packages'
  - '"systemd" in ansible_facts.packages'
  tags:
  - CCE-80784-2
  - DISA-STIG-RHEL-08-040172
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_burstaction
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,CtrlAltDelBurstAction%3Dnone
        mode: 0644
        path: /etc/systemd/system.conf.d/disable_ctrlaltdelete_burstaction.conf
        overwrite: true
OVAL test results details

check if CtrlAltDelBurstAction is set to none  oval:ssg-test_disable_ctrlaltdel_burstaction:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_disable_ctrlaltdel_burstaction:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/systemd/system.conf(\.d/.*\.conf)?$^[\s]*CtrlAltDelBurstAction[\s]*=[\s]*none$1
Disable Ctrl-Alt-Del Reboot Activationxccdf_org.ssgproject.content_rule_disable_ctrlaltdel_reboot highCCE-80785-9

Disable Ctrl-Alt-Del Reboot Activation

Rule IDxccdf_org.ssgproject.content_rule_disable_ctrlaltdel_reboot
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-disable_ctrlaltdel_reboot:def:1
Time2025-11-18T17:05:04-05:00
Severityhigh
Identifiers:

CCE-80785-9

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
osppFAU_GEN.1.2
os-srgSRG-OS-000324-GPOS-00125, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040170
stigrefSV-230529r1017289_rule
Description
By default, SystemD will reboot the system if the Ctrl-Alt-Del key sequence is pressed.

To configure the system to ignore the Ctrl-Alt-Del key sequence from the command line instead of rebooting the system, do either of the following:
ln -sf /dev/null /etc/systemd/system/ctrl-alt-del.target
or
systemctl mask ctrl-alt-del.target


Do not simply delete the /usr/lib/systemd/system/ctrl-alt-del.service file, as this file may be restored during future system updates.
Rationale
A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    systemctl disable ctrl-alt-del.target
    systemctl mask ctrl-alt-del.target
else
    systemctl disable --now ctrl-alt-del.target
    systemctl mask --now ctrl-alt-del.target
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80785-9
  - DISA-STIG-RHEL-08-040170
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_reboot
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

- name: Disable Ctrl-Alt-Del Reboot Activation
  ansible.builtin.systemd:
    name: ctrl-alt-del.target
    force: true
    masked: true
    state: stopped
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80785-9
  - DISA-STIG-RHEL-08-040170
  - NIST-800-171-3.4.5
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - disable_ctrlaltdel_reboot
  - disable_strategy
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: ctrl-alt-del.target
        mask: true
OVAL test results details

Disable Ctrl-Alt-Del key sequence override exists  oval:ssg-test_disable_ctrlaltdel_exists:tst:1  false

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
false/etc/systemd/system/ctrl-alt-del.target/usr/lib/systemd/system/reboot.target
Configure Logind to terminate idle sessions after certain time of inactivityxccdf_org.ssgproject.content_rule_logind_session_timeout mediumCCE-90784-0

Configure Logind to terminate idle sessions after certain time of inactivity

Rule IDxccdf_org.ssgproject.content_rule_logind_session_timeout
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-logind_session_timeout:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-90784-0

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8
cjis5.5.6
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.1.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.3, CIP-007-3 R5.1, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistCM-6(a), AC-17(a), AC-2(5), AC-12, AC-17(a), SC-10, CM-6(a)
nist-csfDE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2
osppFMT_SMF_EXT.1.1
pcidssReq-8.1.8
os-srgSRG-OS-000163-GPOS-00072
anssiR32
stigidRHEL-08-020035
stigrefSV-257258r1069328_rule
Description
To configure logind service to terminate inactive user sessions after 600 seconds, edit the file /etc/systemd/logind.conf. Ensure that there is a section
[Login]
which contains the configuration
StopIdleSessionSec=600
.
Rationale
Terminating an idle session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been let unattended.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.7"; printf "%s\n%s" "$expected" "$real" | sort -VC; } && grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="9.0"; [[ "$real" != "$expected" ]]; } ) || grep -qP "^ID=[\"']?ol[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.7"; printf "%s\n%s" "$expected" "$real" | sort -VC; }; }; then

var_logind_session_timeout='600'



# Try find '[Login]' and 'StopIdleSessionSec' in '/etc/systemd/logind.conf', if it exists, set
# to '$var_logind_session_timeout', if it isn't here, add it, if '[Login]' doesn't exist, add it there
if grep -qzosP '[[:space:]]*\[Login]([^\n\[]*\n+)+?[[:space:]]*StopIdleSessionSec' '/etc/systemd/logind.conf'; then
    
    sed -i "s/StopIdleSessionSec[^(\n)]*/StopIdleSessionSec=$var_logind_session_timeout/" '/etc/systemd/logind.conf'
elif grep -qs '[[:space:]]*\[Login]' '/etc/systemd/logind.conf'; then
    sed -i "/[[:space:]]*\[Login]/a StopIdleSessionSec=$var_logind_session_timeout" '/etc/systemd/logind.conf'
else
    if test -d "/etc/systemd"; then
        printf '%s\n' '[Login]' "StopIdleSessionSec=$var_logind_session_timeout" >> '/etc/systemd/logind.conf'
    else
        echo "Config file directory '/etc/systemd' doesnt exist, not remediating, assuming non-applicability." >&2
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-90784-0
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-020035
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - logind_session_timeout
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
- name: XCCDF Value var_logind_session_timeout # promote to variable
  set_fact:
    var_logind_session_timeout: !!str 600
  tags:
    - always

- name: Set 'StopIdleSessionSec' to '{{ var_logind_session_timeout }}' in the [Login]
    section of '/etc/systemd/logind.conf'
  community.general.ini_file:
    path: /etc/systemd/logind.conf
    section: Login
    option: StopIdleSessionSec
    value: '{{ var_logind_session_timeout }}'
    create: true
    mode: 420
  when:
  - '"kernel" in ansible_facts.packages'
  - ( ansible_distribution == 'RedHat' and ansible_distribution_version is version('8.7',
    '>=') and ansible_distribution == 'RedHat' and ansible_distribution_version is
    version('9.0', '!=') ) or ansible_distribution == 'OracleLinux' and ansible_distribution_version
    is version('8.7', '>=')
  tags:
  - CCE-90784-0
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-020035
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - logind_session_timeout
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

tests the value of StopIdleSessionSec setting in the /etc/systemd/logind.conf file  oval:ssg-test_logind_session_timeout:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_logind_session_timeout:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/systemd/logind.conf^\s*\[Login\].*(?:\n\s*[^[\s].*)*\n^\s*StopIdleSessionSec[ \t]*=[ \t]*(.+?)[ \t]*(?:$|#)1

The configuration file /etc/systemd/logind.conf exists for logind_session_timeout  oval:ssg-test_logind_session_timeout_config_file_exists:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/etc/systemd/logind.confregular001123rw-r--r-- 
Require Authentication for Emergency Systemd Targetxccdf_org.ssgproject.content_rule_require_emergency_target_auth mediumCCE-82186-8

Require Authentication for Emergency Systemd Target

Rule IDxccdf_org.ssgproject.content_rule_require_emergency_target_auth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-require_emergency_target_auth:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-82186-8

References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.1.1, 3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistIA-2, AC-3, CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
os-srgSRG-OS-000080-GPOS-00048
stigidRHEL-08-010152
stigrefSV-244523r1017329_rule
Description
Emergency mode is intended as a system recovery method, providing a single user root access to the system during a failed boot sequence.

By default, Emergency mode is protected by requiring a password and is set in /usr/lib/systemd/system/emergency.service.
Rationale
This prevents attackers with physical access from trivially bypassing security on the machine and gaining root access. Such accesses are further prevented by configuring the bootloader password.
OVAL test results details

Tests that /usr/lib/systemd/systemd-sulogin-shell was not removed from the default systemd emergency.service to ensure that a password must be entered to access single user mode  oval:ssg-test_require_emergency_service:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/emergency.serviceExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency

Tests that the systemd emergency.service is in the emergency.target  oval:ssg-test_require_emergency_service_emergency_target:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/emergency.targetRequires=emergency.service

look for emergency.target in /etc/systemd/system  oval:ssg-test_no_custom_emergency_target:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_no_custom_emergency_target:obj:1 of type file_object
BehaviorsPathFilename
no value/etc/systemd/system^emergency.target$

look for emergency.service in /etc/systemd/system  oval:ssg-test_no_custom_emergency_service:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_no_custom_emergency_service:obj:1 of type file_object
BehaviorsPathFilename
no value/etc/systemd/system^emergency.service$

Look for drop in config files for emergency.service  oval:ssg-test_require_emergency_target_auth_drop_in_config_exist:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_require_emergency_target_auth_drop_in_config_exist:obj:1 of type file_object
PathFilename
/etc/systemd/system/emergency.service.d^.*\.conf$

Tests that /usr/lib/systemd/systemd-sulogin-shell was not removed from the default systemd emergency.service to ensure that a password must be entered to access single user mode  oval:ssg-test_require_emergency_service_drop_in:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_require_emergency_service_drop_in:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/systemd/system/emergency.service.d^.*\.conf$^ExecStart=\-/usr/lib/systemd/systemd-sulogin-shell[\s]+emergency1
Require Authentication for Single User Modexccdf_org.ssgproject.content_rule_require_singleuser_auth mediumCCE-80855-0

Require Authentication for Single User Mode

Rule IDxccdf_org.ssgproject.content_rule_require_singleuser_auth
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-require_singleuser_auth:def:1
Time2025-11-18T17:05:04-05:00
Severitymedium
Identifiers:

CCE-80855-0

References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.1.1, 3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistIA-2, AC-3, CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048
stigidRHEL-08-010151
stigrefSV-230236r1017055_rule
Description
Single-user mode is intended as a system recovery method, providing a single user root access to the system by providing a boot option at startup.

By default, single-user mode is protected by requiring a password and is set in /usr/lib/systemd/system/rescue.service.
Rationale
This prevents attackers with physical access from trivially bypassing security on the machine and gaining root access. Such accesses are further prevented by configuring the bootloader password.
OVAL test results details

Tests that /usr/lib/systemd/systemd-sulogin-shell was not removed from the default systemd rescue.service to ensure that a password must be entered to access single user mode  oval:ssg-test_require_rescue_service_distro:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/usr/lib/systemd/system/rescue.serviceExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue

Check that there is no override file for rescue.service with Execstart - directive  oval:ssg-test_rescue_service_not_overridden:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_require_rescue_service_override:obj:1 of type textfilecontent54_object
BehaviorsPathFilenamePatternInstance
no value/etc/systemd/system/rescue.service.d^.*\.conf$^.*ExecStart\s?=\s+.*ExecStart\s?=\s?\-?(.*)$1

Tests that/usr/lib/systemd/systemd-sulogin-shell is defined in /etc/systemd/system/rescue.service.d/*.conf  oval:ssg-test_require_rescue_service_override:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_require_rescue_service_override:obj:1 of type textfilecontent54_object
BehaviorsPathFilenamePatternInstance
no value/etc/systemd/system/rescue.service.d^.*\.conf$^.*ExecStart\s?=\s+.*ExecStart\s?=\s?\-?(.*)$1
Set Existing Passwords Maximum Agexccdf_org.ssgproject.content_rule_accounts_password_set_max_life_existing mediumCCE-82473-0

Set Existing Passwords Maximum Age

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_set_max_life_existing
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_set_max_life_existing:def:1
Time2025-11-18T17:05:05-05:00
Severitymedium
Identifiers:

CCE-82473-0

References:
nistIA-5(f), IA-5(1)(d), CM-6(a)
os-srgSRG-OS-000076-GPOS-00044
cis4.5.1.2
pcidss48.3.9, 8.3
stigidRHEL-08-020210
stigrefSV-230367r1038967_rule
Description
Configure non-compliant accounts to enforce a 60-day maximum password lifetime restriction by running the following command:
$ sudo chage -M 60 USER
Rationale
Any password, no matter how complex, can eventually be cracked. Therefore, passwords need to be changed periodically. If the operating system does not limit the lifetime of passwords and force users to change their passwords, there is the risk that the operating system passwords could be compromised.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

var_accounts_maximum_age_login_defs='60'


while IFS= read -r i; do
    
    chage -M $var_accounts_maximum_age_login_defs $i

done <   <(awk -v var="$var_accounts_maximum_age_login_defs" -F: '(/^[^:]+:[^!*]/ && ($5 > var || $5 == "")) {print $1}' /etc/shadow)

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable
  set_fact:
    var_accounts_maximum_age_login_defs: !!str 60
  tags:
    - always

- name: Collect users with not correct maximum time period between password changes
  ansible.builtin.command:
    cmd: awk -F':' '(/^[^:]+:[^!*]/ && ($5 > {{ var_accounts_maximum_age_login_defs
      }} || $5 == "")) {print $1}' /etc/shadow
  register: user_names
  tags:
  - CCE-82473-0
  - DISA-STIG-RHEL-08-020210
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(d)
  - NIST-800-53-IA-5(f)
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.9
  - accounts_password_set_max_life_existing
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Change the maximum time period between password changes
  ansible.builtin.user:
    user: '{{ item }}'
    password_expire_max: '{{ var_accounts_maximum_age_login_defs }}'
  with_items: '{{ user_names.stdout_lines }}'
  when: user_names.stdout_lines | length > 0
  tags:
  - CCE-82473-0
  - DISA-STIG-RHEL-08-020210
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(d)
  - NIST-800-53-IA-5(f)
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.9
  - accounts_password_set_max_life_existing
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Compares a specific field in /etc/shadow with a specific variable value  oval:ssg-test_accounts_password_set_max_life_existing_password_max_life_existing:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/shadowadmin:$6$nrUDkGge5sLM3wPe$DN2totPXG6qF0b9yW0SENzCpVRhhn9w.qzy2vBeJXFtSYVdiNpT4B59XZ4tZqEulOWqZzqivkpbOZ5pXPKVP4.::0:99999:7:::
false/etc/shadowroot:$6$rPitlgvRj6tVxdmk$Q27.JyWwRx8T9P3AIM/LFKpwhy7kvlqlwAdppOhOO6pF9t1idMw1NSJW8t/ua2CNyJzHn3cWFbcHLjxrhcKvk.::0:99999:7:::

Compares a specific field in /etc/shadow with a specific variable value  oval:ssg-test_accounts_password_set_max_life_existing_password_max_life_existing_minimum:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/shadowadmin:$6$nrUDkGge5sLM3wPe$DN2totPXG6qF0b9yW0SENzCpVRhhn9w.qzy2vBeJXFtSYVdiNpT4B59XZ4tZqEulOWqZzqivkpbOZ5pXPKVP4.::0:99999:7:::
true/etc/shadowroot:$6$rPitlgvRj6tVxdmk$Q27.JyWwRx8T9P3AIM/LFKpwhy7kvlqlwAdppOhOO6pF9t1idMw1NSJW8t/ua2CNyJzHn3cWFbcHLjxrhcKvk.::0:99999:7:::

Passwords must have the maximum password age set non-empty in /etc/shadow.  oval:ssg-test_accounts_password_set_max_life_existing_password_max_life_not_empty:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_password_set_max_life_existing_shadow_password_users_max_life_not_existing:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/shadow^(?:[^:]*:)(?:[^\!\*:]+:)(?:[^:]*:){2}():(?:[^:]*:){3}(?:[^:]*)$1
Set Existing Passwords Minimum Agexccdf_org.ssgproject.content_rule_accounts_password_set_min_life_existing mediumCCE-82472-2

Set Existing Passwords Minimum Age

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_set_min_life_existing
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_set_min_life_existing:def:1
Time2025-11-18T17:05:05-05:00
Severitymedium
Identifiers:

CCE-82472-2

References:
nistIA-5(f), IA-5(1)(d), CM-6(a)
os-srgSRG-OS-000075-GPOS-00043
stigidRHEL-08-020180
stigrefSV-230364r1017176_rule
Description
Configure non-compliant accounts to enforce a 24 hours/1 day minimum password lifetime by running the following command:
$ sudo chage -m 1 USER
Rationale
Enforcing a minimum password lifetime helps to prevent repeated password changes to defeat the password reuse or history enforcement requirement. If users are allowed to immediately and continually change their password, the password could be repeatedly changed in a short period of time to defeat the organization's policy regarding password reuse.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

var_accounts_minimum_age_login_defs='1'


while IFS= read -r i; do
    
    chage -m $var_accounts_minimum_age_login_defs $i

done <   <(awk -v var="$var_accounts_minimum_age_login_defs" -F: '(/^[^:]+:[^!*]/ && ($4 < var || $4 == "")) {print $1}' /etc/shadow)

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_accounts_minimum_age_login_defs # promote to variable
  set_fact:
    var_accounts_minimum_age_login_defs: !!str 1
  tags:
    - always

- name: Collect users with not correct minimum time period between password changes
  ansible.builtin.command: |
    awk -F':' '(/^[^:]+:[^!*]/ && ($4 < {{ var_accounts_minimum_age_login_defs }} || $4 == "")) {print $1}' /etc/shadow
  register: user_names
  tags:
  - CCE-82472-2
  - DISA-STIG-RHEL-08-020180
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(d)
  - NIST-800-53-IA-5(f)
  - accounts_password_set_min_life_existing
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Change the minimum time period between password changes
  ansible.builtin.command: |
    chage -m {{ var_accounts_minimum_age_login_defs }} {{ item }}
  with_items: '{{ user_names.stdout_lines }}'
  when: user_names.stdout_lines | length > 0
  tags:
  - CCE-82472-2
  - DISA-STIG-RHEL-08-020180
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(d)
  - NIST-800-53-IA-5(f)
  - accounts_password_set_min_life_existing
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Compares a specific field in /etc/shadow with a specific variable value  oval:ssg-test_accounts_password_set_min_life_existing_password_max_life_existing:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/shadowroot:$6$rPitlgvRj6tVxdmk$Q27.JyWwRx8T9P3AIM/LFKpwhy7kvlqlwAdppOhOO6pF9t1idMw1NSJW8t/ua2CNyJzHn3cWFbcHLjxrhcKvk.::0:99999:7:::
true/etc/shadowadmin:$6$nrUDkGge5sLM3wPe$DN2totPXG6qF0b9yW0SENzCpVRhhn9w.qzy2vBeJXFtSYVdiNpT4B59XZ4tZqEulOWqZzqivkpbOZ5pXPKVP4.::0:99999:7:::

Compares a specific field in /etc/shadow with a specific variable value  oval:ssg-test_accounts_password_set_min_life_existing_password_max_life_existing_minimum:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/shadowroot:$6$rPitlgvRj6tVxdmk$Q27.JyWwRx8T9P3AIM/LFKpwhy7kvlqlwAdppOhOO6pF9t1idMw1NSJW8t/ua2CNyJzHn3cWFbcHLjxrhcKvk.::0:99999:7:::
false/etc/shadowadmin:$6$nrUDkGge5sLM3wPe$DN2totPXG6qF0b9yW0SENzCpVRhhn9w.qzy2vBeJXFtSYVdiNpT4B59XZ4tZqEulOWqZzqivkpbOZ5pXPKVP4.::0:99999:7:::

Passwords must have the maximum password age set non-empty in /etc/shadow.  oval:ssg-test_accounts_password_set_min_life_existing_password_max_life_not_empty:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_password_set_min_life_existing_shadow_password_users_max_life_not_existing:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/shadow^(?:[^:]*:)(?:[^\!\*:]+:)(?:[^:]*:)():(?:[^:]*:){4}(?:[^:]*)$1
Verify All Account Password Hashes are Shadowed with SHA512xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed_sha512 mediumCCE-83484-6

Verify All Account Password Hashes are Shadowed with SHA512

Rule IDxccdf_org.ssgproject.content_rule_accounts_password_all_shadowed_sha512
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_password_all_shadowed_sha512:def:1
Time2025-11-18T17:05:05-05:00
Severitymedium
Identifiers:

CCE-83484-6

References:
nistIA-5(1)(c), IA-5(1).1(v), IA-7, IA-7.1
os-srgSRG-OS-000073-GPOS-00041, SRG-OS-000120-GPOS-00061
stigidRHEL-08-010120
stigrefSV-230232r1017051_rule
Description
Verify the operating system requires the shadow password suite configuration be set to encrypt interactive user passwords using a strong cryptographic hash. Check that the interactive user account passwords are using a strong password hash with the following command:
$ sudo cut -d: -f2 /etc/shadow
$6$kcOnRq/5$NUEYPuyL.wghQwWssXRcLRFiiru7f5JPV6GaJhNC2aK5F3PZpE/BCCtwrxRc/AInKMNX3CdMw11m9STiql12f/
Password hashes ! or * indicate inactive accounts not available for logon and are not evaluated. If any interactive user password hash does not begin with $6, this is a finding.
Rationale
Passwords need to be protected at all times, and encryption is the standard method for protecting passwords. If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised.
OVAL test results details

password hashes are shadowed using sha512  oval:ssg-test_accounts_password_all_shadowed_sha512:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_password_all_shadowed_sha512:obj:1 of type shadow_object
UsernameFilterFilterFilter
.*oval:ssg-state_accounts_password_all_shadowed_has_no_password:ste:1oval:ssg-state_accounts_password_all_shadowed_has_locked_password:ste:1oval:ssg-state_accounts_password_all_shadowed_sha512:ste:1
Prevent Login to Accounts With Empty Passwordxccdf_org.ssgproject.content_rule_no_empty_passwords highCCE-80841-0

Prevent Login to Accounts With Empty Password

Rule IDxccdf_org.ssgproject.content_rule_no_empty_passwords
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-no_empty_passwords:def:1
Time2025-11-18T17:05:05-05:00
Severityhigh
Identifiers:

CCE-80841-0

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5
cjis5.5.2
cobit5APO01.06, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.10
cui3.1.1, 3.1.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistIA-5(1)(a), IA-5(c), CM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5
osppFIA_UAU.1
pcidssReq-8.2.3
os-srgSRG-OS-000480-GPOS-00227
cis4.4.3.4.1
pcidss48.3.1, 8.3
stigidRHEL-08-020332, RHEL-08-020331
stigrefSV-244541r1017347_rule, SV-268322r1017568_rule
Description
If an account is configured for password authentication but does not have an assigned password, it may be possible to log into the account without authentication. Remove any instances of the nullok in /etc/pam.d/system-auth and /etc/pam.d/password-auth to prevent logins with empty passwords.
Rationale
If an account has an empty password, anyone could log in and run commands with the privileges of that account. Accounts with empty passwords should never be used in operational environments.
Warnings
warning  If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report. Note that this rule is not applicable for systems running within a container. Having user with empty password within a container is not considered a risk, because it should not be possible to directly login into a container anyway.

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature without-nullok

authselect apply-changes -b
else
    
if grep -qP "^\s*auth\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/system-auth"; then
    sed -i -E --follow-symlinks "s/(.*auth.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/system-auth"
fi
    
if grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/system-auth"; then
    sed -i -E --follow-symlinks "s/(.*password.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/system-auth"
fi
    
if grep -qP "^\s*auth\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/password-auth"; then
    sed -i -E --follow-symlinks "s/(.*auth.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/password-auth"
fi
    
if grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/password-auth"; then
    sed -i -E --follow-symlinks "s/(.*password.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/password-auth"
fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80841-0
  - CJIS-5.5.2
  - DISA-STIG-RHEL-08-020331
  - DISA-STIG-RHEL-08-020332
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

- name: Prevent Login to Accounts With Empty Password - Check if system relies on
    authselect
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80841-0
  - CJIS-5.5.2
  - DISA-STIG-RHEL-08-020331
  - DISA-STIG-RHEL-08-020332
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

- name: Prevent Login to Accounts With Empty Password - Remediate using authselect
  block:

  - name: Prevent Login to Accounts With Empty Password - Check integrity of authselect
      current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Prevent Login to Accounts With Empty Password - Informative message based
      on the authselect integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Prevent Login to Accounts With Empty Password - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Prevent Login to Accounts With Empty Password - Ensure "without-nullok"
      feature is enabled using authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature without-nullok
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("without-nullok")

  - name: Prevent Login to Accounts With Empty Password - Ensure authselect changes
      are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"kernel" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80841-0
  - CJIS-5.5.2
  - DISA-STIG-RHEL-08-020331
  - DISA-STIG-RHEL-08-020332
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

- name: Prevent Login to Accounts With Empty Password - Remediate directly editing
    PAM files
  ansible.builtin.replace:
    dest: '{{ item }}'
    regexp: nullok
  loop:
  - /etc/pam.d/system-auth
  - /etc/pam.d/password-auth
  when:
  - '"kernel" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80841-0
  - CJIS-5.5.2
  - DISA-STIG-RHEL-08-020331
  - DISA-STIG-RHEL-08-020332
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IA-5(1)(a)
  - NIST-800-53-IA-5(c)
  - PCI-DSS-Req-8.2.3
  - PCI-DSSv4-8.3
  - PCI-DSSv4-8.3.1
  - configure_strategy
  - high_severity
  - low_complexity
  - medium_disruption
  - no_empty_passwords
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
        mode: 0644
        path: /etc/pam.d/password-auth
        overwrite: true
      - contents:
          source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
        mode: 0644
        path: /etc/pam.d/system-auth
        overwrite: true
OVAL test results details

make sure nullok is not used in /etc/pam.d/system-auth  oval:ssg-test_no_empty_passwords:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/pam.d/system-auth auth required pam_env.so auth required pam_faildelay.so delay=2000000 auth sufficient pam_fprintd.so auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular auth [default=1 ignore=ignore success=ok] pam_localuser.so auth sufficient pam_unix.so nullok auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular auth sufficient pam_sss.so forward_pass auth required pam_deny.so account required pam_unix.so account sufficient pam_localuser.so account sufficient pam_usertype.so issystem account [default=bad success=ok user_unknown=ignore] pam_sss.so account required pam_permit.so password requisite pam_pwquality.so local_users_only password sufficient pam_unix.so sha512 shadow nullok use_authtok
not evaluated/etc/pam.d/password-auth auth required pam_env.so auth required pam_faildelay.so delay=2000000 auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular auth [default=1 ignore=ignore success=ok] pam_localuser.so auth sufficient pam_unix.so nullok auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular auth sufficient pam_sss.so forward_pass auth required pam_deny.so account required pam_unix.so account sufficient pam_localuser.so account sufficient pam_usertype.so issystem account [default=bad success=ok user_unknown=ignore] pam_sss.so account required pam_permit.so password requisite pam_pwquality.so local_users_only password sufficient pam_unix.so sha512 shadow nullok use_authtok
Ensure There Are No Accounts With Blank or Null Passwordsxccdf_org.ssgproject.content_rule_no_empty_passwords_etc_shadow highCCE-85953-8

Ensure There Are No Accounts With Blank or Null Passwords

Rule IDxccdf_org.ssgproject.content_rule_no_empty_passwords_etc_shadow
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-no_empty_passwords_etc_shadow:def:1
Time2025-11-18T17:05:05-05:00
Severityhigh
Identifiers:

CCE-85953-8

References:
nistCM-6(b), CM-6.1(iv)
os-srgSRG-OS-000480-GPOS-00227
cis6.2.2
pcidss42.2.2, 2.2
stigidRHEL-08-010121
stigrefSV-251706r1017359_rule
Description
Check the "/etc/shadow" file for blank passwords with the following command:
$ sudo awk -F: '!$2 {print $1}' /etc/shadow
If the command returns any results, this is a finding. Configure all accounts on the system to have a password or lock the account with the following commands: Perform a password reset:
$ sudo passwd [username]
Lock an account:
$ sudo passwd -l [username]
Rationale
If an account has an empty password, anyone could log in and run commands with the privileges of that account. Accounts with empty passwords should never be used in operational environments.
Warnings
warning  Note that this rule is not applicable for systems running within a container. Having user with empty password within a container is not considered a risk, because it should not be possible to directly login into a container anyway.
OVAL test results details

make sure there aren't blank or null passwords in /etc/shadow  oval:ssg-test_no_empty_passwords_etc_shadow:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_no_empty_passwords_etc_shadow:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/shadow^[^:]+::.*$1
Verify Only Root Has UID 0xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero highCCE-80649-7

Verify Only Root Has UID 0

Rule IDxccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_no_uid_except_zero:def:1
Time2025-11-18T17:05:05-05:00
Severityhigh
Identifiers:

CCE-80649-7

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.10
cui3.1.1, 3.1.5
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistIA-2, AC-6(5), IA-4(b)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5
pcidssReq-8.5
os-srgSRG-OS-000480-GPOS-00227
cis6.2.9
pcidss48.2.1, 8.2
stigidRHEL-08-040200
stigrefSV-230534r1017296_rule
Description
If any account other than root has a UID of 0, this misconfiguration should be investigated and the accounts other than root should be removed or have their UID changed.
If the account is associated with system commands or applications the UID should be changed to one greater than "0" but less than "1000." Otherwise assign a UID greater than "1000" that has not already been assigned.
Rationale
An account has root authority if it has a UID of 0. Multiple accounts with a UID of 0 afford more opportunity for potential intruders to guess a password for a privileged account. Proper configuration of sudo is recommended to afford multiple system administrators access to root privileges in an accountable manner.
OVAL test results details

test that there are no accounts with UID 0 except root in the /etc/passwd file  oval:ssg-test_accounts_no_uid_except_root:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_no_uid_except_root:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/passwd^(?!root:)[^:]*:[^:]*:01
Only Authorized Local User Accounts Exist on Operating Systemxccdf_org.ssgproject.content_rule_accounts_authorized_local_users mediumCCE-85987-6

Only Authorized Local User Accounts Exist on Operating System

Rule IDxccdf_org.ssgproject.content_rule_accounts_authorized_local_users
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_authorized_local_users:def:1
Time2025-11-18T17:05:05-05:00
Severitymedium
Identifiers:

CCE-85987-6

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-020320
stigrefSV-230379r1017190_rule
Description
Enterprise Application tends to use the server or virtual machine exclusively. Besides the default operating system user, there should be only authorized local users required by the installed software groups and applications that exist on the operating system. The authorized user list can be customized in the refine value variable var_accounts_authorized_local_users_regex. OVAL regular expression is used for the user list. Configure the system so all accounts on the system are assigned to an active system, application, or user account. Remove accounts that do not support approved system activities or that allow for a normal user to perform administrative-level actions. To remove unauthorized system accounts, use the following command:
$ sudo userdel unauthorized_user
Rationale
Accounts providing no operational purpose provide additional opportunities for system compromise. Unnecessary accounts include user accounts for individuals not requiring access to the system and application accounts for applications not installed on the system.
Warnings
warning  Automatic remediation of this control is not available due to the unique requirements of each system.
OVAL test results details

query /etc/passwd  oval:ssg-test_accounts_authorized_local_users:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/passwdnobody:
true/etc/passwdrtkit:
false/etc/passwdpipewire:
true/etc/passwdpulse:
false/etc/passwddnsmasq:
true/etc/passwdqemu:
false/etc/passwdclevis:
true/etc/passwdusbmuxd:
false/etc/passwdgluster:
true/etc/passwdrpc:
true/etc/passwdchrony:
true/etc/passwdsetroubleshoot:
true/etc/passwdsaslauth:
true/etc/passwdlibstoragemgmt:
true/etc/passwdsssd:
false/etc/passwdcockpit-ws:
false/etc/passwdcockpit-wsinstance:
false/etc/passwdflatpak:
true/etc/passwddbus:
true/etc/passwdsystemd-coredump:
true/etc/passwdsystemd-resolve:
true/etc/passwdtss:
true/etc/passwdpolkitd:
true/etc/passwdgeoclue:
true/etc/passwdunbound:
true/etc/passwdftp:
true/etc/passwdhalt:
true/etc/passwdshutdown:
true/etc/passwdoperator:
true/etc/passwdgames:
true/etc/passwdsync:
true/etc/passwdlp:
true/etc/passwdadm:
true/etc/passwddaemon:
true/etc/passwdbin:
true/etc/passwdmail:
true/etc/passwdcolord:
true/etc/passwdrpcuser:
true/etc/passwdgdm:
true/etc/passwdgnome-initial-setup:
true/etc/passwdsshd:
true/etc/passwdavahi:
true/etc/passwdtcpdump:
false/etc/passwdadmin:
Ensure the Default Bash Umask is Set Correctlyxccdf_org.ssgproject.content_rule_accounts_umask_etc_bashrc mediumCCE-81036-6

Ensure the Default Bash Umask is Set Correctly

Rule IDxccdf_org.ssgproject.content_rule_accounts_umask_etc_bashrc
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_umask_etc_bashrc:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-81036-6

References:
cis-csc18
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03
isa-62443-20094.3.4.3.3
iso27001-2013A.14.1.1, A.14.2.1, A.14.2.5, A.6.1.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-6(1), CM-6(a)
nist-csfPR.IP-2
os-srgSRG-OS-000480-GPOS-00228, SRG-OS-000480-GPOS-00227
anssiR36
cis4.5.3.3
stigidRHEL-08-020353
stigrefSV-230385r1017194_rule
Description
To ensure the default umask for users of the Bash shell is set properly, add or correct the umask setting in /etc/bashrc to read as follows:
umask 077
Rationale
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.

# Remediation is applicable only in certain platforms
if rpm --quiet -q bash; then

var_accounts_user_umask='077'






grep -q "^[^#]*\bumask" /etc/bashrc && \
  sed -i -E -e "s/^([^#]*\bumask)[[:space:]]+[[:digit:]]+/\1 $var_accounts_user_umask/g" /etc/bashrc
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/bashrc
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81036-6
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_bashrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_accounts_user_umask # promote to variable
  set_fact:
    var_accounts_user_umask: !!str 077
  tags:
    - always

- name: Check if umask in /etc/bashrc is already set
  ansible.builtin.lineinfile:
    path: /etc/bashrc
    regexp: ^[^#]*\bumask\s+\d+$
    state: absent
  check_mode: true
  changed_when: false
  register: umask_replace
  when: '"bash" in ansible_facts.packages'
  tags:
  - CCE-81036-6
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_bashrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Replace user umask in /etc/bashrc
  ansible.builtin.replace:
    path: /etc/bashrc
    regexp: ^([^#]*\b)umask\s+\d+$
    replace: \g<1>umask {{ var_accounts_user_umask }}
  when:
  - '"bash" in ansible_facts.packages'
  - umask_replace.found > 0
  tags:
  - CCE-81036-6
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_bashrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the Default umask is Appended Correctly
  ansible.builtin.lineinfile:
    create: true
    path: /etc/bashrc
    line: umask {{ var_accounts_user_umask }}
  when:
  - '"bash" in ansible_facts.packages'
  - umask_replace.found == 0
  tags:
  - CCE-81036-6
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_bashrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Verify the existence of var_accounts_user_umask_as_number variable  oval:ssg-test_existence_of_var_accounts_user_umask_as_number_variable:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
not evaluatedoval:ssg-var_accounts_user_umask_umask_as_number:var:163

Test the retrieved /etc/bashrc umask value(s) match the var_accounts_user_umask requirement  oval:ssg-tst_accounts_umask_etc_bashrc:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-var_etc_bashrc_umask_as_number:var:118
Ensure the Default C Shell Umask is Set Correctlyxccdf_org.ssgproject.content_rule_accounts_umask_etc_csh_cshrc mediumCCE-81037-4

Ensure the Default C Shell Umask is Set Correctly

Rule IDxccdf_org.ssgproject.content_rule_accounts_umask_etc_csh_cshrc
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_umask_etc_csh_cshrc:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-81037-4

References:
cis-csc18
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03
isa-62443-20094.3.4.3.3
iso27001-2013A.14.1.1, A.14.2.1, A.14.2.5, A.6.1.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-6(1), CM-6(a)
nist-csfPR.IP-2
os-srgSRG-OS-000480-GPOS-00228, SRG-OS-000480-GPOS-00227
stigidRHEL-08-020353
stigrefSV-230385r1017194_rule
Description
To ensure the default umask for users of the C shell is set properly, add or correct the umask setting in /etc/csh.cshrc to read as follows:
umask 077
Rationale
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.


var_accounts_user_umask='077'


grep -q "^\s*umask" /etc/csh.cshrc && \
  sed -i -E -e "s/^(\s*umask).*/\1 $var_accounts_user_umask/g" /etc/csh.cshrc
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/csh.cshrc
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_accounts_user_umask # promote to variable
  set_fact:
    var_accounts_user_umask: !!str 077
  tags:
    - always

- name: Check if umask in /etc/csh.cshrc is already set
  ansible.builtin.lineinfile:
    path: /etc/csh.cshrc
    regexp: ^(\s*)umask\s+.*
    state: absent
  check_mode: true
  changed_when: false
  register: umask_replace
  tags:
  - CCE-81037-4
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_csh_cshrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Replace user umask in /etc/csh.cshrc
  ansible.builtin.replace:
    path: /etc/csh.cshrc
    regexp: ^(\s*)umask(\s+).*
    replace: \g<1>umask\g<2>{{ var_accounts_user_umask }}
  when: umask_replace.found > 0
  tags:
  - CCE-81037-4
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_csh_cshrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the Default umask is Appended Correctly
  ansible.builtin.lineinfile:
    create: true
    path: /etc/csh.cshrc
    line: umask {{ var_accounts_user_umask }}
  when: umask_replace.found == 0
  tags:
  - CCE-81037-4
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_csh_cshrc
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Verify the existence of var_accounts_user_umask_as_number variable  oval:ssg-test_existence_of_var_accounts_user_umask_as_number_variable:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
not evaluatedoval:ssg-var_accounts_user_umask_umask_as_number:var:163

Test the retrieved /etc/csh.cshrc umask value(s) match the var_accounts_user_umask requirement  oval:ssg-tst_accounts_umask_etc_csh_cshrc:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValueValueValueValueValueValueValueValue
falseoval:ssg-var_etc_csh_cshrc_umask_as_number:var:1221818221818
Ensure the Default Umask is Set Correctly in /etc/profilexccdf_org.ssgproject.content_rule_accounts_umask_etc_profile mediumCCE-81035-8

Ensure the Default Umask is Set Correctly in /etc/profile

Rule IDxccdf_org.ssgproject.content_rule_accounts_umask_etc_profile
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_umask_etc_profile:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-81035-8

References:
cis-csc18
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03
isa-62443-20094.3.4.3.3
iso27001-2013A.14.1.1, A.14.2.1, A.14.2.5, A.6.1.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-6(1), CM-6(a)
nist-csfPR.IP-2
os-srgSRG-OS-000480-GPOS-00228, SRG-OS-000480-GPOS-00227
anssiR36
cis4.5.3.3
stigidRHEL-08-020353
stigrefSV-230385r1017194_rule
Description
To ensure the default umask controlled by /etc/profile is set properly, add or correct the umask setting in /etc/profile to read as follows:
umask 077
Note that /etc/profile also reads scrips within /etc/profile.d directory. These scripts are also valid files to set umask value. Therefore, they should also be considered during the check and properly remediated, if necessary.
Rationale
The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

var_accounts_user_umask='077'


readarray -t profile_files < <(find /etc/profile.d/ -type f -name '*.sh' -or -name 'sh.local')

for file in "${profile_files[@]}" /etc/profile; do
  grep -qE '^[^#]*umask' "$file" && sed -i -E "s/^(\s*umask\s*)[0-7]+/\1$var_accounts_user_umask/g" "$file"
done

if ! grep -qrE '^[^#]*umask' /etc/profile*; then
  echo "umask $var_accounts_user_umask" >> /etc/profile
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_accounts_user_umask # promote to variable
  set_fact:
    var_accounts_user_umask: !!str 077
  tags:
    - always

- name: Ensure the Default Umask is Set Correctly in /etc/profile - Locate Profile
    Configuration Files Where umask Is Defined
  ansible.builtin.find:
    paths:
    - /etc/profile.d
    patterns:
    - sh.local
    - '*.sh'
    contains: ^[\s]*umask\s+\d+
  register: result_profile_d_files
  tags:
  - CCE-81035-8
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_profile
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the Default Umask is Set Correctly in /etc/profile - Replace Existing
    umask Value in Files From /etc/profile.d
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^(\s*)umask\s+\d+
    replace: \1umask {{ var_accounts_user_umask }}
  loop: '{{ result_profile_d_files.files }}'
  register: result_umask_replaced_profile_d
  when: result_profile_d_files.matched
  tags:
  - CCE-81035-8
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_profile
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the Default Umask is Set Correctly in /etc/profile - Ensure umask Is
    Set in /etc/profile if Not Already Set Elsewhere
  ansible.builtin.lineinfile:
    create: true
    mode: 420
    path: /etc/profile
    line: umask {{ var_accounts_user_umask }}
  when: not result_profile_d_files.matched
  tags:
  - CCE-81035-8
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_profile
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure the Default Umask is Set Correctly in /etc/profile - Ensure umask Value
    For All Existing umask Definition in /etc/profile
  ansible.builtin.replace:
    path: /etc/profile
    regexp: ^(\s*)umask\s+\d+
    replace: \1umask {{ var_accounts_user_umask }}
  register: result_umask_replaced_profile
  tags:
  - CCE-81035-8
  - DISA-STIG-RHEL-08-020353
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - accounts_umask_etc_profile
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Verify the existence of var_accounts_user_umask_as_number variable  oval:ssg-test_existence_of_var_accounts_user_umask_as_number_variable:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
not evaluatedoval:ssg-var_accounts_user_umask_umask_as_number:var:163

umask value(s) from profile configuration files match the requirement  oval:ssg-tst_accounts_umask_etc_profile:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValueValueValueValueValueValueValueValue
falseoval:ssg-var_etc_profile_umask_as_number:var:1221818221818
Ensure the Default Umask is Set Correctly For Interactive Usersxccdf_org.ssgproject.content_rule_accounts_umask_interactive_users mediumCCE-84044-7

Ensure the Default Umask is Set Correctly For Interactive Users

Rule IDxccdf_org.ssgproject.content_rule_accounts_umask_interactive_users
Result
error
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_umask_interactive_users:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-84044-7

References:
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00228
stigidRHEL-08-020352
stigrefSV-230384r1017193_rule
Description
Remove the UMASK environment variable from all interactive users initialization files.
Rationale
The umask controls the default access mode assigned to newly created files. A umask of 077 limits new files to mode 700 or less permissive. Although umask can be represented as a four-digit number, the first digit representing special access modes is typically ignored or required to be 0. This requirement applies to the globally configured system defaults and the local interactive user defaults for each account on the system.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

while IFS= read -r dir; do
    while IFS= read -r -d '' file; do
        if [ "$(basename $file)" != ".bash_history" ]; then
            sed -i 's/^\(\s*umask\s*\)/#\1/g' "$file"
        fi
    done <   <(find $dir -maxdepth 1 -type f -name ".*" -print0)
done <   <(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd)

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Ensure interactive local users are the owners of their respective initialization
    files
  ansible.builtin.shell:
    cmd: |-
      for dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534) print $6}' /etc/passwd); do
        for file in $(find $dir -maxdepth 1 -type f -name ".*"); do
          if [ "$(basename $file)" != ".bash_history" ]; then
            sed -i 's/^\(\s*umask\s*\)/#\1/g' $file
          fi
        done
      done
  tags:
  - CCE-84044-7
  - DISA-STIG-RHEL-08-020352
  - accounts_umask_interactive_users
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Umask must not be defined in user initialization files  oval:ssg-test_accounts_umask_interactive_users:tst:1  error

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_umask_interactive_users:obj:1 of type textfilecontent54_object
BehaviorsPathFilenamePatternInstanceFilter
^(?:admin):(?:[^:]*:){4}([^:]+):[^:]*$
/home/admin
Regular expression pattern match failed in file /home/admin/.esd_auth with error -10.
no value^\..*^[\s]*umask\s*1oval:ssg-state_accounts_umask_interactive_users_bash_history:ste:1
Ensure the Logon Failure Delay is Set Correctly in login.defsxccdf_org.ssgproject.content_rule_accounts_logon_fail_delay mediumCCE-84037-1

Ensure the Logon Failure Delay is Set Correctly in login.defs

Rule IDxccdf_org.ssgproject.content_rule_accounts_logon_fail_delay
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_logon_fail_delay:def:1
Time2025-11-18T17:05:05-05:00
Severitymedium
Identifiers:

CCE-84037-1

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistAC-7(b), CM-6(a)
nist-csfPR.IP-1
os-srgSRG-OS-000480-GPOS-00226
stigidRHEL-08-020310
stigrefSV-230378r1017189_rule
Description
To ensure the logon failure delay controlled by /etc/login.defs is set properly, add or correct the FAIL_DELAY setting in /etc/login.defs to read as follows:
FAIL_DELAY 4
Rationale
Increasing the time between a failed authentication attempt and re-prompting to enter credentials helps to slow a single-threaded brute force attack.

# Remediation is applicable only in certain platforms
if rpm --quiet -q shadow-utils; then

var_accounts_fail_delay='4'


# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^FAIL_DELAY")

# shellcheck disable=SC2059
printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_fail_delay"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^FAIL_DELAY\\>" "/etc/login.defs"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^FAIL_DELAY\\>.*/$escaped_formatted_output/gi" "/etc/login.defs"
else
    if [[ -s "/etc/login.defs" ]] && [[ -n "$(tail -c 1 -- "/etc/login.defs" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/login.defs"
    fi
    cce="CCE-84037-1"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/login.defs" >> "/etc/login.defs"
    printf '%s\n' "$formatted_output" >> "/etc/login.defs"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84037-1
  - DISA-STIG-RHEL-08-020310
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - accounts_logon_fail_delay
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
- name: XCCDF Value var_accounts_fail_delay # promote to variable
  set_fact:
    var_accounts_fail_delay: !!str 4
  tags:
    - always

- name: Set accounts logon fail delay
  ansible.builtin.lineinfile:
    dest: /etc/login.defs
    regexp: ^FAIL_DELAY
    line: FAIL_DELAY {{ var_accounts_fail_delay }}
    create: true
  when: '"shadow-utils" in ansible_facts.packages'
  tags:
  - CCE-84037-1
  - DISA-STIG-RHEL-08-020310
  - NIST-800-53-AC-7(b)
  - NIST-800-53-CM-6(a)
  - accounts_logon_fail_delay
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

check FAIL_DELAY in /etc/login.defs  oval:ssg-test_accounts_logon_fail_delay:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_logon_fail_delay:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/login.defs^[\s]*(?i)FAIL_DELAY(?-i)[\s]+([^#\s]*)1
User Initialization Files Must Not Run World-Writable Programsxccdf_org.ssgproject.content_rule_accounts_user_dot_no_world_writable_programs mediumCCE-84039-7

User Initialization Files Must Not Run World-Writable Programs

Rule IDxccdf_org.ssgproject.content_rule_accounts_user_dot_no_world_writable_programs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_user_dot_no_world_writable_programs:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-84039-7

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010660
stigrefSV-230309r1017119_rule
Description
Set the mode on files being executed by the user initialization files with the following command:
$ sudo chmod o-w FILE
Rationale
If user start-up files execute world-writable programs, especially in unprotected directories, they could be maliciously modified to destroy user files or otherwise compromise the system at the user level. If the system is compromised at the user level, it is easier to elevate privileges to eventually compromise the system at the root and network level.
OVAL test results details

Init files do not execute world-writable programs  oval:ssg-test_accounts_user_dot_no_world_writable_programs:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_accounts_user_dot_no_world_writable_programs_init_files:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
^\.[\w\- ]+$
/home/admin
Referenced variable has no values (oval:ssg-var_world_writable_programs_regex:var:1).
1
Ensure that Users Path Contains Only Local Directoriesxccdf_org.ssgproject.content_rule_accounts_user_home_paths_only mediumCCE-84040-5

Ensure that Users Path Contains Only Local Directories

Rule IDxccdf_org.ssgproject.content_rule_accounts_user_home_paths_only
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-84040-5

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010690
stigrefSV-230317r1069320_rule
Description
Ensure that all interactive user initialization files executable search path statements do not contain statements that will reference a working directory other than the users home directory.
Rationale
The executable search path (typically the PATH environment variable) contains a list of directories for the shell to search to find executables. If this path includes the current working directory (other than the users home directory), executables in these directories may be executed instead of system commands. This variable is formatted as a colon-separated list of directories. If there is an empty entry, such as a leading or trailing colon or two consecutive colons, this is interpreted as the current working directory. If deviations from the default system search path for the local interactive user are required, they must be documented with the Information System Security Officer (ISSO).
Evaluation messages
info 
No candidate or applicable check found.
All Interactive Users Must Have A Home Directory Definedxccdf_org.ssgproject.content_rule_accounts_user_interactive_home_directory_defined mediumCCE-84036-3

All Interactive Users Must Have A Home Directory Defined

Rule IDxccdf_org.ssgproject.content_rule_accounts_user_interactive_home_directory_defined
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_user_interactive_home_directory_defined:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-84036-3

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010720
stigrefSV-230320r1017131_rule
Description
Assign home directories to all interactive users that currently do not have a home directory assigned. This rule checks if the home directory is properly defined in a folder which has at least one parent folder, like "user" in "/home/user" or "/remote/users/user". Therefore, this rule will report a finding for home directories like /users, /tmp or /.
Rationale
If local interactive users are not assigned a valid home directory, there is no place for the storage and control of files they should own.
OVAL test results details

All Interactive Users Have A Home Directory Defined  oval:ssg-test_accounts_user_interactive_home_directory_defined:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUsernamePasswordUser idGroup idGcosHome dirLogin shellLast login
trueadminx10001000admin/home/admin/bin/bash1763496526
All Interactive Users Home Directories Must Existxccdf_org.ssgproject.content_rule_accounts_user_interactive_home_directory_exists mediumCCE-83424-2

All Interactive Users Home Directories Must Exist

Rule IDxccdf_org.ssgproject.content_rule_accounts_user_interactive_home_directory_exists
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_user_interactive_home_directory_exists:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-83424-2

References:
os-srgSRG-OS-000480-GPOS-00227
cis6.2.10
stigidRHEL-08-010750
stigrefSV-230323r1017134_rule
Description
Create home directories to all local interactive users that currently do not have a home directory assigned. Use the following commands to create the user home directory assigned in /etc/passwd:
$ sudo mkdir /home/USER
Rationale
If a local interactive user has a home directory defined that does not exist, the user may be given access to the / directory as the current working directory upon logon. This could create a Denial of Service because the user would not be able to access their logon configuration files, and it may give them visibility to system files they normally would not be able to access.
OVAL test results details

Check the existence of interactive users.  oval:ssg-test_accounts_user_interactive_home_directory_exists:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count_fs:var:11

Check the existence of interactive users.  oval:ssg-test_accounts_user_interactive_home_directory_exists_users:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
not evaluatedoval:ssg-var_accounts_user_interactive_home_directory_exists_dirs_count:var:11
All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary Groupxccdf_org.ssgproject.content_rule_accounts_users_home_files_groupownership mediumCCE-86534-5

All User Files and Directories In The Home Directory Must Be Group-Owned By The Primary Group

Rule IDxccdf_org.ssgproject.content_rule_accounts_users_home_files_groupownership
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_users_home_files_groupownership:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-86534-5

References:
os-srgSRG-OS-000480-GPOS-00227
anssiR50
stigidRHEL-08-010741
stigrefSV-244532r1101906_rule
Description
Change the group of a local interactive users files and directories to a group that the interactive user is a member of. To change the group owner of a local interactive users files and directories, use the following command:
$ sudo chgrp USER_GROUP /home/USER/FILE_DIR
This rule ensures every file or directory under the home directory related to an interactive user is group-owned by an interactive user.
Rationale
If a local interactive users files are group-owned by a group of which the user is not a member, unintended users may be able to access them.
Warnings
warning  Due to OVAL limitation, this rule can report a false negative in a specific situation where two interactive users swap the group-ownership of folders or files in their respective home directories.
OVAL test results details

All home directories files are group-owned by a local interactive user  oval:ssg-test_accounts_users_home_files_groupownership:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-card-database.tdbregular10001000696rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-default-sinkregular100010001rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-default-sourceregular100010001rw------- 
true/home/admin/.config/ibus/bus/a3303742af9f4e2db46126a560ba64ef-unix-0regular10001000168rw-rw-r-- 
true/home/admin/.config/dconf/userregular100010002755rw-r--r-- 
true/home/admin/.config/evolution/sources/system-proxy.sourceregular10001000993rw-r--r-- 
true/home/admin/.config/user-dirs.dirsregular10001000633rw------- 
true/home/admin/.config/user-dirs.localeregular100010005rw-rw-r-- 
true/home/admin/.config/gtk-3.0/bookmarksregular10001000137rw-rw-r-- 
true/home/admin/.config/gnome-initial-setup-doneregular100010003rw-rw-r-- 
true/home/admin/.config/pulse/cookieregular10001000256rw------- 
true/home/admin/.bash_profileregular10001000141rw-r--r-- 
true/home/admin/.local/share/gsettings-data-convertregular10001000542rw-rw-r-- 
true/home/admin/.esd_authregular1000100016rw------- 
true/home/admin/.bash_historyregular1000100083rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-device-volumes.tdbregular100010008192rw------- 
true/home/admin/.bashrcregular10001000376rw-r--r-- 
true/home/admin/.bash_logoutregular1000100018rw-r--r-- 
true/home/admin/.ICEauthorityregular10001000310rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-stream-volumes.tdbregular1000100012288rw------- 
true/home/admin/.config/yelp/yelp.cfgregular1000100051rw-rw-r-- 
true/home/admin/.local/share/keyrings/login.keyringregular10001000105rw------- 
true/home/admin/.local/share/keyrings/user.keystoreregular10001000207rw------- 
true/home/admin/.local/share/gnome-shell/gnome-overrides-migratedregular100010000rw-rw-r-- 
true/home/admin/.local/share/gnome-shell/application_stateregular10001000336rw-rw-r-- 
true/home/admin/.cache/tracker/db-version.txtregular100010002rw-r--r-- 
true/home/admin/.local/share/evolution/addressbook/system/contacts.dbregular1000100086016rw-r--r-- 
true/home/admin/.local/share/evolution/calendar/system/calendar.icsregular10001000173rw-r--r-- 
true/home/admin/.local/share/flatpak/repo/configregular1000100068rw-r--r-- 
true/home/admin/.local/share/flatpak/.changedregular100010000rw-r--r-- 
true/home/admin/.local/share/gnome-settings-daemon/input-sources-convertedregular100010000rw-rw-r-- 
true/home/admin/.local/share/icc/edid-bb6ad72dc802b000932c73ad20996ae5.iccregular100010001432rw-rw-r-- 
true/home/admin/.local/share/tracker/data/.meta.isrunningregular100010000rw-r----- 
true/home/admin/.local/share/tracker/data/tracker-store.ontology.journalregular1000100075517rw-r----- 
true/home/admin/.local/share/tracker/data/tracker-store.journalregular1000100079592rw-r----- 
true/home/admin/.local/share/gvfs-metadata/homeregular1000100064rw------- 
true/home/admin/.local/share/gvfs-metadata/home-af128344.logregular1000100032768rw-r--r-- 
true/home/admin/.local/share/yelp/storage/saltregular100010008rw------- 
true/home/admin/.local/share/recently-used.xbelregular10001000218rw------- 
true/home/admin/.cache/mesa_shader_cache/indexregular100010001310728rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/5f/d8298a3725bf70225b0384673170359ace58afregular100010001543rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/13/80083bc220cc9418616ada7ac38d81aba9213cregular100010003630rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/c9/b8bfeca47c0954725d360599f598cd80e11f90regular100010002508rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/c9/7651c47b14da2733c33c7796fd4f1ccdc82e5eregular100010002879rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/e7/0226fc99ac1638b53092f0f47305857fef50bfregular100010002557rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/dc/7e09b56064c0f69b82b5fa2d00d5cd737a2d8cregular100010002260rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/82/d4e0505c4429bcbd7d36019ed09d661d3b261dregular100010002594rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/64/a8f4045b6da45abedd8dd28f071859e99e0f8cregular100010001143rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/97/4ec2adc922c989a93cd33a6dd0a0c9b923f230regular100010001769rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/db/25027328d66afd9858c84ae47b72d9772fe0d5regular100010002068rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/db/ff3f1f81fe99cbb14f2fbc9f382985e85f3f01regular100010002509rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/17/dad01da172bf1fc6a13d1198ba55e16cc617b5regular100010002598rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/cb/53f2a08fa855e7e7bf43028c517e38202ec18bregular100010002595rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/cb/c60bf45218e6de8b99820f1d95eee9f4244689regular100010001949rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/48/7cd40893b850c1acf301813e0d4c0878e0613dregular100010003092rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/32/44f9b1dca4cb740b95a9afc6b7c929172bb1e5regular100010002595rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/28/1ec490d275a9743074619b85ab127e9d1ac0bdregular100010002173rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/94/5a48e1d23a46e32de63e3810fa3e70347ea693regular100010002368rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/b3/88ed3cb31d176ac3577f688d3493645d007132regular100010001664rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/b3/c877d9b95842e9e077fd79fc002dcd46b10785regular100010002899rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/b3/a094c4d782a38f563cdeffe8dbd0a9c8532e41regular100010001532rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/b1/9be343ee339fc8a97071a2fbaba07ca567feddregular100010002595rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/65/9439b776c6cca5a8ca2ceb365c6d04a3b5b10eregular100010002392rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/65/708f56f12badd4e51e6a318aca5e72951f264bregular100010002509rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/3f/ac50428121ea23c2b2414265f1d1871820f599regular100010001772rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/f1/e35f93655e5aacb9906f91b3322d8b40647fb3regular100010001700rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/fc/00d07064ee76849da402788a1d73a380fdf7a7regular100010002793rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/16/8f72c0fa78d0bccfc690c6f2648d6b06492fd3regular100010002801rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/ab/38cfdd29c051e25ee755e8025dfadc9945f52dregular100010002803rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/46/65d2de6432b149430693125cb893bea534f49fregular100010001794rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/c3/ea8ecaddf8b85c35b4a12563641c4b68a8b0dbregular100010003340rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/aa/5ec8257cb7f9be3b19ec91d3d3155d084cee91regular100010002932rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/a4/49e07e418356cf61bdeee4fa96686b91da06dcregular100010003123rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/3e/e7bcf7e5b2e702f46f8533d4c4c11c742e1216regular100010003349rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/0e/d42af29193f189c2461c169042a697b5b05454regular100010003348rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/e5/a8d3ed698d074750ede9345d0c506bb237fc6fregular100010003126rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/1c/42e16db205a10f25453156e4d048399f41a9b4regular100010002894rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/1c/827a598d470daadb15a2716025dd2d13ee130aregular100010002285rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/e2/d439b7f601871812a0a6e142960fa43135a6ccregular100010001615rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/79/17aa1678029212376906b629094714e2437723regular100010003476rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/f4/8d4bad33d6c12f8e3cf75a0f02839d53288616regular100010002215rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/25/514495518f48d6a2518cadb65a7ec49ad9fa0dregular100010003478rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/3a/e470cd4926a24df581a776bd66e6ebe495ecearegular100010001362rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/74/3026d4fe459b153b68a79d244c2e681ee33006regular100010003331rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/9c/c507a6f82b1215dec751900a0d5c3e58f3d022regular100010002597rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/fb/48366d95b59f8d8028d171d404ee8e5af356f1regular100010002049rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/00/5f678c6283a4a34cf1fc27b6a51ffb81486221regular100010003204rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/c1/7a46ff95792eda1548d0c64bdc9cfc4f64c143regular100010002786rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/36/07d64a0c5f69ead05dc0a1db0d1ef180750c59regular100010002814rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/af/13d4b54efaad33530d72591136493cd310c98aregular100010001667rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/85/81c2e02bcb8ad6c87bf66a5641264857cc04d3regular100010002597rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/ea/ccc78fe127733227691eb62f48106a5d5b5e15regular100010003406rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/b2/0c02de9d675e2158e142192875007b4b50a035regular100010002932rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/69/49088df4840f7309176940690299423fa1501aregular100010002808rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/fe/685c95bdcdcdde2cebf99847388ae51dda9204regular100010002813rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/87/9bd57cb5ff02ea82d4b36c963eea87164f2871regular100010003648rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/43/e5714150f23a2397133e4a36031e1131f95921regular100010002617rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/c4/cde9211b73f126d7dcc19d55bfaa2a8543c706regular100010002592rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/78/e0e2c06333480f358c946de32d67c1c9be0851regular100010002369rw-r--r-- 
true/home/admin/.cache/mesa_shader_cache/7a/aa604f26329f827798f9c76cbc7de1c0070bb4regular100010003408rw-r--r-- 
... and 29 more items.
All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissivexccdf_org.ssgproject.content_rule_accounts_users_home_files_permissions mediumCCE-85888-6

All User Files and Directories In The Home Directory Must Have Mode 0750 Or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_accounts_users_home_files_permissions
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-accounts_users_home_files_permissions:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-85888-6

References:
os-srgSRG-OS-000480-GPOS-00227
anssiR50
stigidRHEL-08-010731
stigrefSV-244531r1017338_rule
Description
Set the mode on files and directories in the local interactive user home directory with the following command:
$ sudo chmod 0750 /home/USER/FILE_DIR
Files that begin with a "." are excluded from this requirement.
Rationale
If a local interactive user files have excessive permissions, unintended users may be able to access or modify them.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

for home_dir in $(awk -F':' '{ if ($3 >= 1000 && $3 != 65534 && $6 != "/") print $6 }' /etc/passwd); do
    # Only update the permissions when necessary. This will avoid changing the inode timestamp when
    # the permission is already defined as expected, therefore not impacting in possible integrity
    # check systems that also check inodes timestamps.
    find "$home_dir" -perm /7027 \! -type l -exec chmod u-s,g-w-s,o=- {} \;
done

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Get all local users from /etc/passwd
  ansible.builtin.getent:
    database: passwd
    split: ':'
  tags:
  - CCE-85888-6
  - DISA-STIG-RHEL-08-010731
  - accounts_users_home_files_permissions
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Create local_users variable from the getent output
  ansible.builtin.set_fact:
    local_users: '{{ ansible_facts.getent_passwd|dict2items }}'
  tags:
  - CCE-85888-6
  - DISA-STIG-RHEL-08-010731
  - accounts_users_home_files_permissions
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Test for existence home directories to avoid creating them.
  ansible.builtin.stat:
    path: '{{ item.value[4] }}'
  register: path_exists
  loop: '{{ local_users }}'
  when:
  - item.value[1]|int >= 1000
  - item.value[1]|int != 65534
  - item.value[4] != "/"
  tags:
  - CCE-85888-6
  - DISA-STIG-RHEL-08-010731
  - accounts_users_home_files_permissions
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure interactive local users have proper permissions on their respective
    home directories
  ansible.builtin.file:
    path: '{{ item.0.value[4] }}'
    mode: u-s,g-w-s,o=-
    follow: false
    recurse: true
  loop: '{{ local_users|zip(path_exists.results)|list }}'
  when: item.1.stat is defined and item.1.stat.exists
  tags:
  - CCE-85888-6
  - DISA-STIG-RHEL-08-010731
  - accounts_users_home_files_permissions
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

All files into home directories have proper permissions  oval:ssg-test_accounts_users_home_files_permissions_files:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-card-database.tdbregular10001000696rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-default-sinkregular100010001rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-default-sourceregular100010001rw------- 
false/home/admin/.config/ibus/bus/a3303742af9f4e2db46126a560ba64ef-unix-0regular10001000168rw-rw-r-- 
false/home/admin/.config/dconf/userregular100010002755rw-r--r-- 
false/home/admin/.config/evolution/sources/system-proxy.sourceregular10001000993rw-r--r-- 
true/home/admin/.config/user-dirs.dirsregular10001000633rw------- 
false/home/admin/.config/user-dirs.localeregular100010005rw-rw-r-- 
false/home/admin/.config/gtk-3.0/bookmarksregular10001000137rw-rw-r-- 
false/home/admin/.config/gnome-initial-setup-doneregular100010003rw-rw-r-- 
true/home/admin/.config/pulse/cookieregular10001000256rw------- 
false/home/admin/.local/share/gsettings-data-convertregular10001000542rw-rw-r-- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-device-volumes.tdbregular100010008192rw------- 
true/home/admin/.config/pulse/a3303742af9f4e2db46126a560ba64ef-stream-volumes.tdbregular1000100012288rw------- 
false/home/admin/.config/yelp/yelp.cfgregular1000100051rw-rw-r-- 
true/home/admin/.local/share/keyrings/login.keyringregular10001000105rw------- 
true/home/admin/.local/share/keyrings/user.keystoreregular10001000207rw------- 
false/home/admin/.local/share/gnome-shell/gnome-overrides-migratedregular100010000rw-rw-r-- 
false/home/admin/.local/share/gnome-shell/application_stateregular10001000336rw-rw-r-- 
false/home/admin/.cache/tracker/db-version.txtregular100010002rw-r--r-- 
false/home/admin/.local/share/evolution/addressbook/system/contacts.dbregular1000100086016rw-r--r-- 
false/home/admin/.local/share/evolution/calendar/system/calendar.icsregular10001000173rw-r--r-- 
false/home/admin/.local/share/flatpak/repo/configregular1000100068rw-r--r-- 
false/home/admin/.local/share/gnome-settings-daemon/input-sources-convertedregular100010000rw-rw-r-- 
false/home/admin/.local/share/icc/edid-bb6ad72dc802b000932c73ad20996ae5.iccregular100010001432rw-rw-r-- 
true/home/admin/.local/share/tracker/data/tracker-store.ontology.journalregular1000100075517rw-r----- 
true/home/admin/.local/share/tracker/data/tracker-store.journalregular1000100079592rw-r----- 
true/home/admin/.local/share/gvfs-metadata/homeregular1000100064rw------- 
false/home/admin/.local/share/gvfs-metadata/home-af128344.logregular1000100032768rw-r--r-- 
true/home/admin/.local/share/yelp/storage/saltregular100010008rw------- 
true/home/admin/.local/share/recently-used.xbelregular10001000218rw------- 
false/home/admin/.cache/mesa_shader_cache/indexregular100010001310728rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/5f/d8298a3725bf70225b0384673170359ace58afregular100010001543rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/13/80083bc220cc9418616ada7ac38d81aba9213cregular100010003630rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/c9/b8bfeca47c0954725d360599f598cd80e11f90regular100010002508rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/c9/7651c47b14da2733c33c7796fd4f1ccdc82e5eregular100010002879rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/e7/0226fc99ac1638b53092f0f47305857fef50bfregular100010002557rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/dc/7e09b56064c0f69b82b5fa2d00d5cd737a2d8cregular100010002260rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/82/d4e0505c4429bcbd7d36019ed09d661d3b261dregular100010002594rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/64/a8f4045b6da45abedd8dd28f071859e99e0f8cregular100010001143rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/97/4ec2adc922c989a93cd33a6dd0a0c9b923f230regular100010001769rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/db/25027328d66afd9858c84ae47b72d9772fe0d5regular100010002068rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/db/ff3f1f81fe99cbb14f2fbc9f382985e85f3f01regular100010002509rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/17/dad01da172bf1fc6a13d1198ba55e16cc617b5regular100010002598rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/cb/53f2a08fa855e7e7bf43028c517e38202ec18bregular100010002595rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/cb/c60bf45218e6de8b99820f1d95eee9f4244689regular100010001949rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/48/7cd40893b850c1acf301813e0d4c0878e0613dregular100010003092rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/32/44f9b1dca4cb740b95a9afc6b7c929172bb1e5regular100010002595rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/28/1ec490d275a9743074619b85ab127e9d1ac0bdregular100010002173rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/94/5a48e1d23a46e32de63e3810fa3e70347ea693regular100010002368rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/b3/88ed3cb31d176ac3577f688d3493645d007132regular100010001664rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/b3/c877d9b95842e9e077fd79fc002dcd46b10785regular100010002899rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/b3/a094c4d782a38f563cdeffe8dbd0a9c8532e41regular100010001532rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/b1/9be343ee339fc8a97071a2fbaba07ca567feddregular100010002595rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/65/9439b776c6cca5a8ca2ceb365c6d04a3b5b10eregular100010002392rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/65/708f56f12badd4e51e6a318aca5e72951f264bregular100010002509rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/3f/ac50428121ea23c2b2414265f1d1871820f599regular100010001772rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/f1/e35f93655e5aacb9906f91b3322d8b40647fb3regular100010001700rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/fc/00d07064ee76849da402788a1d73a380fdf7a7regular100010002793rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/16/8f72c0fa78d0bccfc690c6f2648d6b06492fd3regular100010002801rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/ab/38cfdd29c051e25ee755e8025dfadc9945f52dregular100010002803rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/46/65d2de6432b149430693125cb893bea534f49fregular100010001794rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/c3/ea8ecaddf8b85c35b4a12563641c4b68a8b0dbregular100010003340rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/aa/5ec8257cb7f9be3b19ec91d3d3155d084cee91regular100010002932rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/a4/49e07e418356cf61bdeee4fa96686b91da06dcregular100010003123rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/3e/e7bcf7e5b2e702f46f8533d4c4c11c742e1216regular100010003349rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/0e/d42af29193f189c2461c169042a697b5b05454regular100010003348rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/e5/a8d3ed698d074750ede9345d0c506bb237fc6fregular100010003126rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/1c/42e16db205a10f25453156e4d048399f41a9b4regular100010002894rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/1c/827a598d470daadb15a2716025dd2d13ee130aregular100010002285rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/e2/d439b7f601871812a0a6e142960fa43135a6ccregular100010001615rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/79/17aa1678029212376906b629094714e2437723regular100010003476rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/f4/8d4bad33d6c12f8e3cf75a0f02839d53288616regular100010002215rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/25/514495518f48d6a2518cadb65a7ec49ad9fa0dregular100010003478rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/3a/e470cd4926a24df581a776bd66e6ebe495ecearegular100010001362rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/74/3026d4fe459b153b68a79d244c2e681ee33006regular100010003331rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/9c/c507a6f82b1215dec751900a0d5c3e58f3d022regular100010002597rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/fb/48366d95b59f8d8028d171d404ee8e5af356f1regular100010002049rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/00/5f678c6283a4a34cf1fc27b6a51ffb81486221regular100010003204rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/c1/7a46ff95792eda1548d0c64bdc9cfc4f64c143regular100010002786rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/36/07d64a0c5f69ead05dc0a1db0d1ef180750c59regular100010002814rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/af/13d4b54efaad33530d72591136493cd310c98aregular100010001667rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/85/81c2e02bcb8ad6c87bf66a5641264857cc04d3regular100010002597rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/ea/ccc78fe127733227691eb62f48106a5d5b5e15regular100010003406rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/b2/0c02de9d675e2158e142192875007b4b50a035regular100010002932rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/69/49088df4840f7309176940690299423fa1501aregular100010002808rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/fe/685c95bdcdcdde2cebf99847388ae51dda9204regular100010002813rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/87/9bd57cb5ff02ea82d4b36c963eea87164f2871regular100010003648rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/43/e5714150f23a2397133e4a36031e1131f95921regular100010002617rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/c4/cde9211b73f126d7dcc19d55bfaa2a8543c706regular100010002592rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/78/e0e2c06333480f358c946de32d67c1c9be0851regular100010002369rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/7a/aa604f26329f827798f9c76cbc7de1c0070bb4regular100010003408rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/90/8304b135eca94640c368c91ffcf9ae229b377cregular100010003129rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/a1/2cf522fe011455aee2d42ae9f06fe3c19bf53cregular100010002900rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/9a/a65237b272dc7a226b430752508efadc321ba7regular100010001847rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/55/f171d961e48d38cf8b4acbae982df390c38bc0regular100010003340rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/2f/42919383be8d05918f582f02148d3c83236c93regular100010002596rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/1f/e3879c8a0c233edb5c5d273963d99d63aaa9acregular100010001498rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/2a/e50e892dae867d6a3887daba0f915ccf3c8fa1regular100010002522rw-r--r-- 
false/home/admin/.cache/mesa_shader_cache/99/6256d1d07a9e0d0b6375da90e6001b3751742dregular100010002180rw-r--r-- 
... and 20 more items.

All directories into home directories have proper permissions  oval:ssg-test_accounts_users_home_files_permissions_dirs:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
false/home/admin/.mozilla/directory1000100039rwxr-xr-x 
false/home/admin/.mozilla/extensions/directory100010006rwxr-xr-x 
false/home/admin/.mozilla/plugins/directory100010006rwxr-xr-x 
true/home/admin/.config/directory100010004096rwx------ 
true/home/admin/.config/gconf/directory100010006rwx------ 
true/home/admin/.config/gnome-session/directory1000100027rwx------ 
true/home/admin/.config/gnome-session/saved-session/directory100010006rwx------ 
true/home/admin/.config/pulse/directory100010004096rwx------ 
true/home/admin/.config/ibus/directory1000100017rwx------ 
false/home/admin/.local/share/flatpak/directory1000100044rwxr-xr-x 
true/home/admin/.local/share/sounds/directory100010006rwx------ 
true/home/admin/.local/directory1000100019rwx------ 
false/home/admin/Desktop/directory100010006rwxr-xr-x 
true/home/admin/.cache/directory100010004096rwx------ 
true/home/admin/.config/ibus/bus/directory1000100053rwx------ 
false/home/admin/.config/dconf/directory1000100018rwxr-xr-x 
true/home/admin/.config/evolution/directory1000100021rwx------ 
true/home/admin/.config/evolution/sources/directory1000100033rwx------ 
false/home/admin/.config/goa-1.0/directory100010006rwxr-xr-x 
true/home/admin/.config/gtk-3.0/directory1000100023rwx------ 
false/home/admin/.config/yelp/directory1000100022rwxrwxr-x 
true/home/admin/.local/share/directory100010004096rwx------ 
true/home/admin/.local/share/keyrings/directory1000100048rwx------ 
true/home/admin/.cache/libgweather/directory100010006rwx------ 
true/home/admin/.local/share/gnome-shell/directory1000100063rwx------ 
true/home/admin/.local/share/evolution/directory1000100079rwx------ 
false/home/admin/.local/share/evolution/addressbook/directory1000100033rwxr-xr-x 
false/home/admin/.local/share/evolution/addressbook/trash/directory100010006rwxr-xr-x 
true/home/admin/.local/share/evolution/addressbook/system/directory1000100039rwx------ 
true/home/admin/.local/share/evolution/addressbook/system/photos/directory100010006rwx------ 
false/home/admin/.local/share/evolution/calendar/directory1000100033rwxr-xr-x 
false/home/admin/.local/share/evolution/calendar/trash/directory100010006rwxr-xr-x 
true/home/admin/.local/share/evolution/calendar/system/directory1000100026rwx------ 
false/home/admin/.local/share/evolution/mail/directory1000100019rwxr-xr-x 
false/home/admin/.local/share/evolution/mail/trash/directory100010006rwxr-xr-x 
false/home/admin/.local/share/evolution/memos/directory1000100019rwxr-xr-x 
false/home/admin/.local/share/evolution/memos/trash/directory100010006rwxr-xr-x 
false/home/admin/.local/share/evolution/tasks/directory1000100019rwxr-xr-x 
false/home/admin/.local/share/evolution/tasks/trash/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/db/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/directory1000100089rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/tmp/directory1000100019rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/tmp/cache/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/extensions/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/state/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/refs/directory1000100049rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/refs/heads/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/refs/mirrors/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/refs/remotes/directory100010006rwxr-xr-x 
false/home/admin/.local/share/flatpak/repo/objects/directory100010006rwxr-xr-x 
true/home/admin/.local/share/applications/directory100010006rwx------ 
false/home/admin/.local/share/gnome-settings-daemon/directory1000100037rwxr-xr-x 
false/home/admin/.local/share/icc/directory1000100055rwxrwxr-x 
false/home/admin/.local/share/tracker/directory1000100018rwxr-xr-x 
false/home/admin/.local/share/tracker/data/directory1000100096rwxr-xr-x 
true/home/admin/.local/share/gvfs-metadata/directory1000100043rwx------ 
false/home/admin/.local/share/yelp/directory1000100046rwxrwxr-x 
false/home/admin/.local/share/yelp/deviceidhashsalts/directory1000100015rwxrwxr-x 
false/home/admin/.local/share/yelp/deviceidhashsalts/1/directory100010006rwxrwxr-x 
false/home/admin/.local/share/yelp/storage/directory1000100018rwxrwxr-x 
true/home/admin/.cache/mesa_shader_cache/directory100010004096rwx------ 
true/home/admin/.cache/mesa_shader_cache/5f/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/13/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/c9/directory1000100098rwx------ 
true/home/admin/.cache/mesa_shader_cache/e7/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/dc/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/82/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/64/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/97/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/db/directory1000100098rwx------ 
true/home/admin/.cache/mesa_shader_cache/17/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/cb/directory1000100098rwx------ 
true/home/admin/.cache/mesa_shader_cache/48/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/32/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/28/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/94/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/b3/directory10001000144rwx------ 
true/home/admin/.cache/mesa_shader_cache/b1/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/65/directory1000100098rwx------ 
true/home/admin/.cache/mesa_shader_cache/3f/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/f1/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/fc/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/16/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/ab/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/46/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/c3/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/aa/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/a4/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/3e/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/0e/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/e5/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/1c/directory1000100098rwx------ 
true/home/admin/.cache/mesa_shader_cache/e2/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/79/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/f4/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/25/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/3a/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/74/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/9c/directory1000100052rwx------ 
true/home/admin/.cache/mesa_shader_cache/fb/directory1000100052rwx------ 
... and 61 more items.
All Interactive User Home Directories Must Be Group-Owned By The Primary Groupxccdf_org.ssgproject.content_rule_file_groupownership_home_directories mediumCCE-83434-1

All Interactive User Home Directories Must Be Group-Owned By The Primary Group

Rule IDxccdf_org.ssgproject.content_rule_file_groupownership_home_directories
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_groupownership_home_directories:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-83434-1

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010740
stigrefSV-230322r1017133_rule
Description
Change the group owner of interactive users home directory to the group found in /etc/passwd. To change the group owner of interactive users home directory, use the following command:
$ sudo chgrp USER_GROUP /home/USER
This rule ensures every home directory related to an interactive user is group-owned by an interactive user. It also ensures that interactive users are group-owners of one and only one home directory.
Rationale
If the Group Identifier (GID) of a local interactive users home directory is not the same as the primary GID of the user, this would allow unauthorized access to the users files, and users that share the same group may not be able to access files that they legitimately should.
Warnings
warning  Due to OVAL limitation, this rule can report a false negative in a specific situation where two interactive users swap the group-ownership of their respective home directories.
OVAL test results details

All home directories are group-owned by a local interactive group  oval:ssg-test_file_groupownership_home_directories:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
true/home/admin/directory100010004096rwx------ 
Ensure All User Initialization Files Have Mode 0740 Or Less Permissivexccdf_org.ssgproject.content_rule_file_permission_user_init_files_root mediumCCE-86106-2

Ensure All User Initialization Files Have Mode 0740 Or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_file_permission_user_init_files_root
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permission_user_init_files_root:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-86106-2

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010770
stigrefSV-230325r1017136_rule
Description
Set the mode of the user initialization files, including the root user, to 0740 with the following commands:
$ sudo chmod 0740 /root/.INIT_FILE
$ sudo chmod 0740 /home/USER/.INIT_FILE
Rationale
Local initialization files are used to configure the user's shell environment upon logon. Malicious modification of these files could compromise accounts upon logon.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict

var_user_initialization_files_regex='^\.[\w\- ]+$'


readarray -t interactive_users < <(awk -F: '$3==0 || $3>=1000   {print $1}' /etc/passwd)
readarray -t interactive_users_home < <(awk -F: '$3==0 || $3>=1000   {print $6}' /etc/passwd)
readarray -t interactive_users_shell < <(awk -F: '$3==0 || $3>=1000   {print $7}' /etc/passwd)

USERS_IGNORED_REGEX='nobody|nfsnobody'

for (( i=0; i<"${#interactive_users[@]}"; i++ )); do
    if ! grep -qP "$USERS_IGNORED_REGEX" <<< "${interactive_users[$i]}" && \
        [ "${interactive_users_shell[$i]}" != "/sbin/nologin" ]; then

        readarray -t init_files < <(find "${interactive_users_home[$i]}" -maxdepth 1 \
            -exec basename {} \; | grep -P "$var_user_initialization_files_regex")
        for file in "${init_files[@]}"; do
            chmod u-s,g-wxs,o= "${interactive_users_home[$i]}/$file"
        done
    fi
done

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: XCCDF Value var_user_initialization_files_regex # promote to variable
  set_fact:
    var_user_initialization_files_regex: !!str ^\.[\w\- ]+$
  tags:
    - always

- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Gather
    User Info
  ansible.builtin.getent:
    database: passwd
  tags:
  - CCE-86106-2
  - DISA-STIG-RHEL-08-010770
  - file_permission_user_init_files_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Find
    Init Files
  ansible.builtin.find:
    paths: '{{ item.value[4] }}'
    pattern: '{{ var_user_initialization_files_regex }}'
    hidden: true
    use_regex: true
  with_dict: '{{ ansible_facts.getent_passwd }}'
  when:
  - item.value[4] != "/sbin/nologin"
  - item.key not in ["nobody", "nfsnobody"]
  - item.value[1] | int >= 1000 or item.key == "root"
  register: found_init_files
  tags:
  - CCE-86106-2
  - DISA-STIG-RHEL-08-010770
  - file_permission_user_init_files_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure All User Initialization Files Have Mode 0740 Or Less Permissive - Fix
    Init Files Permissions
  ansible.builtin.file:
    path: '{{ item.1.path }}'
    mode: u-s,g-wxs,o=
  loop: '{{ q(''ansible.builtin.subelements'', found_init_files.results, ''files'',
    {''skip_missing'': True}) }}'
  tags:
  - CCE-86106-2
  - DISA-STIG-RHEL-08-010770
  - file_permission_user_init_files_root
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

Init files have mode 0740 or less permissive  oval:ssg-test_file_permission_user_init_files_root:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
false/root/.bash_logoutregular0018rw-r--r-- 
true/root/.viminforegular007941rw------- 
false/root/.tcshrcregular00129rw-r--r-- 
false/root/.cshrcregular00100rw-r--r-- 
true/root/.bash_historyregular00138rw------- 
false/root/.bashrcregular00176rw-r--r-- 
false/root/.bash_profileregular00176rw-r--r-- 
false/home/admin/.bash_profileregular10001000141rw-r--r-- 
true/home/admin/.esd_authregular1000100016rw------- 
true/home/admin/.bash_historyregular1000100083rw------- 
false/home/admin/.bashrcregular10001000376rw-r--r-- 
false/home/admin/.bash_logoutregular1000100018rw-r--r-- 
true/home/admin/.ICEauthorityregular10001000310rw------- 
true/home/admin/.viminforegular10001000872rw------- 
All Interactive User Home Directories Must Have mode 0750 Or Less Permissivexccdf_org.ssgproject.content_rule_file_permissions_home_directories mediumCCE-84038-9

All Interactive User Home Directories Must Have mode 0750 Or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_home_directories
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_home_directories:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-84038-9

References:
os-srgSRG-OS-000480-GPOS-00227
cis6.2.10
stigidRHEL-08-010730
stigrefSV-230321r1017132_rule
Description
Change the mode of interactive users home directories to 0750. To change the mode of interactive users home directory, use the following command:
$ sudo chmod 0750 /home/USER
Rationale
Excessive permissions on local interactive user home directories may allow unauthorized access to user files by other users.
OVAL test results details

All home directories have proper permissions  oval:ssg-test_file_permissions_home_directories:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
true/home/admin/directory100010004096rwx------ 
Enable authselectxccdf_org.ssgproject.content_rule_enable_authselect mediumCCE-88248-0

Enable authselect

Rule IDxccdf_org.ssgproject.content_rule_enable_authselect
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-enable_authselect:def:1
Time2025-11-18T17:05:00-05:00
Severitymedium
Identifiers:

CCE-88248-0

References:
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
nistAC-3
osppFIA_UAU.1, FIA_AFL.1
os-srgSRG-OS-000480-GPOS-00227
anssiR31
cisenable_authselect
pcidss48.3.4, 8.3
stigidneeded_rules
Description
Configure user authentication setup to use the authselect tool. If authselect profile is selected, the rule will enable the sssd profile.
Rationale
Authselect is a successor to authconfig. It is a tool to select system authentication and identity sources from a list of supported profiles instead of letting the administrator manually build the PAM stack. That way, it avoids potential breakage of configuration, as it ships several tested profiles that are well tested and supported to solve different use-cases.
Warnings
warning  If the sudo authselect select command returns an error informing that the chosen profile cannot be selected, it is probably because PAM files have already been modified by the administrator. If this is the case, in order to not overwrite the desired changes made by the administrator, the current PAM settings should be investigated before forcing the selection of the chosen authselect profile.
OVAL test results details

The 'fingerprint-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_fingerprint_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/fingerprint-auth/etc/authselect/fingerprint-auth

The 'password-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_password_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/password-auth/etc/authselect/password-auth

The 'postlogin' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_postlogin_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/postlogin/etc/authselect/postlogin

The 'smartcard-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_smartcard_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/smartcard-auth/etc/authselect/smartcard-auth

The 'system-auth' PAM config is a symlink to its authselect counterpart  oval:ssg-test_pam_system_symlinked_to_authselect:tst:1  true

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
true/etc/pam.d/system-auth/etc/authselect/system-auth
Set the Boot Loader Admin Username to a Non-Default Valuexccdf_org.ssgproject.content_rule_grub2_admin_username highCCE-83561-1

Set the Boot Loader Admin Username to a Non-Default Value

Rule IDxccdf_org.ssgproject.content_rule_grub2_admin_username
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_admin_username:def:1
Time2025-11-18T17:05:26-05:00
Severityhigh
Identifiers:

CCE-83561-1

References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
os-srgSRG-OS-000080-GPOS-00048
stigidRHEL-08-010149
stigrefSV-244522r1017328_rule
Description
The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

To maximize the protection, select a password-protected superuser account with unique name, and modify the /etc/grub.d/01_users configuration file to reflect the account name change.

Do not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users
The line mentioned above must be followed by the line
export superusers
so that the superusers is honored.

Once the superuser account has been added, update the grub.cfg file by running:
grub2-mkconfig -o /boot/grub2/grub.cfg
Rationale
Having a non-default grub superuser username makes password-guessing attacks less effective.
Warnings
warning  To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
OVAL test results details

superuser is defined in /boot/grub2/grub.cfg. Superuser is not equal to other system account nor root, admin, administrator  oval:ssg-test_bootloader_superuser_differ_from_other_users:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_bootloader_unique_superuser:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/grub2/grub.cfg^[\s]*set[\s]+superusers="(?i)\b(?!(?:root|admin|administrator)\b)(\w+)".*\n[\s]*export[\s]+superusers[\s]*$1
Set Boot Loader Password in grub2xccdf_org.ssgproject.content_rule_grub2_password highCCE-80828-7

Set Boot Loader Password in grub2

Rule IDxccdf_org.ssgproject.content_rule_grub2_password
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_password:def:1
Time2025-11-18T17:05:26-05:00
Severityhigh
Identifiers:

CCE-80828-7

References:
cis-csc1, 11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048
anssiR5
cis1.3.1
stigidRHEL-08-010150
stigrefSV-230235r1017054_rule
Description
The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

Since plaintext passwords are a security risk, generate a hash for the password by running the following command:
# grub2-setpassword
When prompted, enter the password that was selected.

Rationale
Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode.
Warnings
warning  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
OVAL test results details

make sure a password is defined in /boot/grub2/user.cfg  oval:ssg-test_grub2_password_usercfg:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_password_usercfg:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/grub2/user.cfg^[\s]*GRUB2_PASSWORD=grub\.pbkdf2\.sha512.*$1
Set the UEFI Boot Loader Admin Username to a Non-Default Valuexccdf_org.ssgproject.content_rule_grub2_uefi_admin_username mediumCCE-83542-1

Set the UEFI Boot Loader Admin Username to a Non-Default Value

Rule IDxccdf_org.ssgproject.content_rule_grub2_uefi_admin_username
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-83542-1

References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
os-srgSRG-OS-000080-GPOS-00048
stigidRHEL-08-010141
stigrefSV-244521r1017327_rule
Description
The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

To maximize the protection, select a password-protected superuser account with unique name, and modify the /etc/grub.d/01_users configuration file to reflect the account name change.

It is highly suggested not to use common administrator account names like root, admin, or administrator for the grub2 superuser account.

Change the superuser to a different username (The default is 'root').
$ sed -i 's/\(set superusers=\).*/\1"<unique user ID>"/g' /etc/grub.d/01_users
The line mentioned above must be followed by the line
export superusers
so that the superusers is honored.

Once the superuser account has been added, update the grub.cfg file by running:
grub2-mkconfig -o /boot/grub2/grub.cfg
Rationale
Having a non-default grub superuser username makes password-guessing attacks less effective.
Warnings
warning  To prevent hard-coded admin usernames, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
Set the UEFI Boot Loader Passwordxccdf_org.ssgproject.content_rule_grub2_uefi_password highCCE-80829-5

Set the UEFI Boot Loader Password

Rule IDxccdf_org.ssgproject.content_rule_grub2_uefi_password
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:05:26-05:00
Severityhigh
Identifiers:

CCE-80829-5

References:
cis-csc11, 12, 14, 15, 16, 18, 3, 5
cobit5DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06
cui3.4.5
hipaa164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7
iso27001-2013A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.PT-3
osppFIA_UAU.1
os-srgSRG-OS-000080-GPOS-00048
anssiR5
cis1.3.1
stigidRHEL-08-010140
stigrefSV-230234r1017053_rule
Description
The grub2 boot loader should have a superuser account and password protection enabled to protect boot-time settings.

Since plaintext passwords are a security risk, generate a hash for the password by running the following command:
# grub2-setpassword
When prompted, enter the password that was selected.

Rationale
Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode.
Warnings
warning  To prevent hard-coded passwords, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above. Also, do NOT manually add the superuser account and password to the grub.cfg file as the grub2-mkconfig command overwrites this file.
The system must booted with init_on_free=1xccdf_org.ssgproject.content_rule_grub2_init_on_free mediumCCE-90682-6

The system must booted with init_on_free=1

Rule IDxccdf_org.ssgproject.content_rule_grub2_init_on_free
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_init_on_free:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-90682-6

References:
nistSC-3
os-srgSRG-OS-000134-GPOS-00068
stigidRHEL-08-010423
stigrefSV-230279r1069286_rule
Description
Setting init_on_free=1 on boot guarantees that pages and heap objects are initialized right after they're freed, so it won't be possible to access stale data by using a dangling pointer.
Rationale
init_on_free is a Linux kernel boot parameter that enhances security by initializing memory regions when they are freed, preventing data leakage. This process ensures that stale data in freed memory cannot be accessed by malicious programs.

# Remediation is applicable only in certain platforms
if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel ) && { rpm --quiet -q grub2-common; }; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "init_on_free" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"init_on_free=[^\"]*\"(.*]\s*)/\1\"init_on_free=1\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"init_on_free=1\"]" >> "$KARGS_DIR/10-init_on_free.toml"
    fi
else

    grubby --update-kernel=ALL --args=init_on_free=1 --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-90682-6
  - DISA-STIG-RHEL-08-010423
  - NIST-800-53-SC-3
  - grub2_init_on_free
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="init_on_free=1"
  when:
  - ( "grub2-common" in ansible_facts.packages and "kernel" in ansible_facts.packages
    )
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - CCE-90682-6
  - DISA-STIG-RHEL-08-010423
  - NIST-800-53-SC-3
  - grub2_init_on_free
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "init_on_free=1"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader init_on_free=1
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of init_on_free=1. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_init_on_free_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_init_on_free_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters init_on_free=1 in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_init_on_free_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters init_on_free=1 in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_init_on_free_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_init_on_free_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for init_on_free=1 in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_init_on_free_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for init_on_free=1 in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_init_on_free_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_init_on_free_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Enable Kernel Page-Table Isolation (KPTI)xccdf_org.ssgproject.content_rule_grub2_pti_argument lowCCE-82194-2

Enable Kernel Page-Table Isolation (KPTI)

Rule IDxccdf_org.ssgproject.content_rule_grub2_pti_argument
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_pti_argument:def:1
Time2025-11-18T17:05:26-05:00
Severitylow
Identifiers:

CCE-82194-2

References:
nistSI-16
os-srgSRG-OS-000433-GPOS-00193, SRG-OS-000095-GPOS-00049
anssiR8
stigidRHEL-08-040004
stigrefSV-230491r1017274_rule
Description
To enable Kernel page-table isolation, add the argument pti=on to the default GRUB 2 command line for the Linux operating system. To ensure that pti=on is added as a kernel command line argument to newly installed kernels, add pti=on to the default Grub2 command line for Linux operating systems. Modify the line within /etc/default/grub as shown below:
GRUB_CMDLINE_LINUX="... pti=on ..."
Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --args="pti=on"
Rationale
Kernel page-table isolation is a kernel feature that mitigates the Meltdown security vulnerability and hardens the kernel against attempts to bypass kernel address space layout randomization (KASLR).

# Remediation is applicable only in certain platforms
if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel ); then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "pti" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"pti=[^\"]*\"(.*]\s*)/\1\"pti=on\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"pti=on\"]" >> "$KARGS_DIR/10-pti.toml"
    fi
else

    grubby --update-kernel=ALL --args=pti=on --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82194-2
  - DISA-STIG-RHEL-08-040004
  - NIST-800-53-SI-16
  - grub2_pti_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="pti=on"
  when: ( "grub2-common" in ansible_facts.packages and "kernel" in ansible_facts.packages
    )
  tags:
  - CCE-82194-2
  - DISA-STIG-RHEL-08-040004
  - NIST-800-53-SI-16
  - grub2_pti_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "pti=on"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader pti=on
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of pti=on. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_pti_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_pti_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters pti=on in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_pti_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters pti=on in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_pti_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_pti_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for pti=on in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_pti_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for pti=on in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_pti_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_pti_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Disable vsyscallsxccdf_org.ssgproject.content_rule_grub2_vsyscall_argument mediumCCE-80946-7

Disable vsyscalls

Rule IDxccdf_org.ssgproject.content_rule_grub2_vsyscall_argument
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_vsyscall_argument:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-80946-7

References:
nistCM-7(a)
osppFPT_ASLR_EXT.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000134-GPOS-00068
stigidRHEL-08-010422
stigrefSV-230278r1017091_rule
Description
To disable use of virtual syscalls, add the argument vsyscall=none to the default GRUB 2 command line for the Linux operating system. To ensure that vsyscall=none is added as a kernel command line argument to newly installed kernels, add vsyscall=none to the default Grub2 command line for Linux operating systems. Modify the line within /etc/default/grub as shown below:
GRUB_CMDLINE_LINUX="... vsyscall=none ..."
Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --args="vsyscall=none"
Rationale
Virtual Syscalls provide an opportunity of attack for a user who has control of the return instruction pointer.
Warnings
warning  The vsyscall emulation is only available on x86_64 architecture (CONFIG_X86_VSYSCALL_EMULATION) making this rule not applicable to other CPU architectures.

# Remediation is applicable only in certain platforms
if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel ) && { ( grep -sqE "^.*\.x86_64$" /proc/sys/kernel/osrelease || grep -sqE "^x86_64$" /proc/sys/kernel/arch; ); }; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "vsyscall" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"vsyscall=[^\"]*\"(.*]\s*)/\1\"vsyscall=none\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"vsyscall=none\"]" >> "$KARGS_DIR/10-vsyscall.toml"
    fi
else

    grubby --update-kernel=ALL --args=vsyscall=none --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80946-7
  - DISA-STIG-RHEL-08-010422
  - NIST-800-53-CM-7(a)
  - grub2_vsyscall_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="vsyscall=none"
  when:
  - ( "grub2-common" in ansible_facts.packages and "kernel" in ansible_facts.packages
    )
  - ansible_architecture == "x86_64"
  tags:
  - CCE-80946-7
  - DISA-STIG-RHEL-08-010422
  - NIST-800-53-CM-7(a)
  - grub2_vsyscall_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "vsyscall=none"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader vsyscall=none
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of vsyscall=none. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_vsyscall_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_vsyscall_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters vsyscall=none in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_vsyscall_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters vsyscall=none in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_vsyscall_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_vsyscall_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for vsyscall=none in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_vsyscall_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for vsyscall=none in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_vsyscall_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_vsyscall_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Ensure cron Is Logging To Rsyslogxccdf_org.ssgproject.content_rule_rsyslog_cron_logging mediumCCE-80859-2

Ensure cron Is Logging To Rsyslog

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_cron_logging
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_cron_logging:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-80859-2

References:
cis-csc1, 14, 15, 16, 3, 5, 6
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS05.04, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1
ism0988, 1405
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.15.2.1, A.15.2.2
nistCM-6(a)
nist-csfID.SC-4, PR.PT-1
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-030010
stigrefSV-230387r1017195_rule
Description
Cron logging must be implemented to spot intrusions or trace cron job status. If cron is not logging to rsyslog, it can be implemented by adding the following to the RULES section of /etc/rsyslog.conf: If the legacy syntax is used:
cron.*                                                  /var/log/cron
If the modern syntax (RainerScript) is used:
cron.* action(type="omfile" file="/var/log/cron")
Rationale
Cron logging can be used to trace the successful or unsuccessful execution of cron jobs. It can also be used to spot intrusions into the use of the cron facility by unauthorized and malicious users.
OVAL test results details

cron is configured in /etc/rsyslog.conf  oval:ssg-test_cron_logging_rsyslog:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/rsyslog.confcron.* /var/log/cron # Everybody gets emergency messages

cron is configured in /etc/rsyslog.conf using RainerScript  oval:ssg-test_cron_logging_rsyslog_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_cron_logging_rsyslog_rainer:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf(?m)^\s*cron\.\*\s+action\(\s*.*(?i)\btype\b(?-i)="omfile"\s*.*(?i)\bfile\b(?-i)="/var/log/cron"\s*.*\)\s*$1

cron is configured in /etc/rsyslog.d  oval:ssg-test_cron_logging_rsyslog_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_cron_logging_rsyslog_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*$^[\s]*cron\.\*[\s]+/var/log/cron\s*(?:#.*)?$1

cron is configured in /etc/rsyslog.d using RainerScript  oval:ssg-test_cron_logging_rsyslog_dir_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_cron_logging_rsyslog_dir_rainer:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*$(?m)^\s*cron\.\*\s+action\(\s*.*(?i)\btype\b(?-i)="omfile"\s*.*(?i)\bfile\b(?-i)="/var/log/cron"\s*.*\)\s*$1
Ensure Rsyslog Authenticates Off-Loaded Audit Recordsxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_actionsendstreamdriverauthmode mediumCCE-86339-9

Ensure Rsyslog Authenticates Off-Loaded Audit Records

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_actionsendstreamdriverauthmode
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_encrypt_offload_actionsendstreamdriverauthmode:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-86339-9

References:
nistAU-4(1)
os-srgSRG-OS-000342-GPOS-00133, SRG-OS-000479-GPOS-00224
stigidRHEL-08-030720
stigrefSV-230482r1069330_rule
Description
Rsyslogd is a system utility providing support for message logging. Support for both internet and UNIX domain sockets enables this utility to support both local and remote logging. Couple this utility with gnutls (which is a secure communications library implementing the SSL, TLS and DTLS protocols), and you have a method to securely encrypt and off-load auditing. When using rsyslogd to off-load logs the remote system must be authenticated. Set the following configuration option in /etc/rsyslog.conf or in a file in /etc/rsyslog.d (using legacy syntax):
$ActionSendStreamDriverAuthMode x509/name
Alternatively, use the RainerScript syntax:
action(type="omfwd" Target="some.example.com" StreamDriverAuthMode="x509/name")
Rationale
The audit records generated by Rsyslog contain valuable information regarding system configuration, user authentication, and other such information. Audit records should be protected from unauthorized access.

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && rpm --quiet -q rsyslog; then

sed -i '/^.*\$ActionSendStreamDriverAuthMode.*/d' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2> /dev/null

if [ -e "/etc/rsyslog.d/stream_driver_auth.conf" ] ; then
    
    LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverAuthMode /Id" "/etc/rsyslog.d/stream_driver_auth.conf"
else
    touch "/etc/rsyslog.d/stream_driver_auth.conf"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/rsyslog.d/stream_driver_auth.conf"

cp "/etc/rsyslog.d/stream_driver_auth.conf" "/etc/rsyslog.d/stream_driver_auth.conf.bak"
# Insert at the end of the file
printf '%s\n' "\$ActionSendStreamDriverAuthMode x509/name" >> "/etc/rsyslog.d/stream_driver_auth.conf"
# Clean up after ourselves.
rm "/etc/rsyslog.d/stream_driver_auth.conf.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86339-9
  - DISA-STIG-RHEL-08-030720
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_actionsendstreamdriverauthmode

- name: Ensure Rsyslog Authenticates Off-Loaded Audit Records
  block:

  - name: Deduplicate values from /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: false
      regexp: (?i)^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s
      state: absent

  - name: Check if /etc/rsyslog.d exists
    ansible.builtin.stat:
      path: /etc/rsyslog.d
    register: _etc_rsyslog_d_exists

  - name: Check if the parameter $ActionSendStreamDriverAuthMode is present in /etc/rsyslog.d
    ansible.builtin.find:
      paths: /etc/rsyslog.d
      recurse: 'yes'
      follow: 'no'
      contains: ^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s
    register: _etc_rsyslog_d_has_parameter
    when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir

  - name: Remove parameter from files in /etc/rsyslog.d
    ansible.builtin.lineinfile:
      path: '{{ item.path }}'
      create: false
      regexp: (?i)^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s
      state: absent
    with_items: '{{ _etc_rsyslog_d_has_parameter.files }}'
    when: _etc_rsyslog_d_has_parameter.matched

  - name: Insert correct line to /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: true
      regexp: (?i)^\s*{{ "$ActionSendStreamDriverAuthMode"| regex_escape }}\s
      line: $ActionSendStreamDriverAuthMode x509/name
      state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-86339-9
  - DISA-STIG-RHEL-08-030720
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_actionsendstreamdriverauthmode
OVAL test results details

Check if $ActionSendStreamDriverAuthMode x509/name is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\$ActionSendStreamDriverAuthMode x509/name$1

Check if StreamDriverAuthMode is set to x509/name in /etc/rsyslog.conf using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_rainer:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\s*action\(.*(?i)\btype\b(?-i)="omfwd".*(?i)\bStreamDriverAuthMode\b(?-i)="x509/name".*\)\s*$1

Check if $ActionSendStreamDriverAuthMode x509/name is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\$ActionSendStreamDriverAuthMode x509/name$1

Check if StreamDriverAuthMode is set to x509/name in files in /etc/rsyslog.d using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_dir_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdriverauthmode_action_send_stream_driver_auth_mode_dir_rainer:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\s*action\(.*(?i)\btype\b(?-i)="omfwd".*(?i)\bStreamDriverAuthMode\b(?-i)="x509/name".*\)\s*$1
Ensure Rsyslog Encrypts Off-Loaded Audit Recordsxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_actionsendstreamdrivermode mediumCCE-86098-1

Ensure Rsyslog Encrypts Off-Loaded Audit Records

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_actionsendstreamdrivermode
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_encrypt_offload_actionsendstreamdrivermode:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-86098-1

References:
nistAU-4(1)
os-srgSRG-OS-000342-GPOS-00133, SRG-OS-000479-GPOS-00224
stigidRHEL-08-030710
stigrefSV-230481r958754_rule
Description
Rsyslogd is a system utility providing support for message logging. Support for both internet and UNIX domain sockets enables this utility to support both local and remote logging. Couple this utility with gnutls (which is a secure communications library implementing the SSL, TLS and DTLS protocols), and you have a method to securely encrypt and off-load auditing. When using rsyslogd to off-load logs off a encrpytion system must be used. Set the following configuration option in /etc/rsyslog.conf or in a file in /etc/rsyslog.d (using legacy syntax):
$ActionSendStreamDriverMode 1
Alternatively, use the RainerScript syntax:
action(type="omfwd" ... StreamDriverMode="1")
Rationale
The audit records generated by Rsyslog contain valuable information regarding system configuration, user authentication, and other such information. Audit records should be protected from unauthorized access.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && rpm --quiet -q rsyslog; then

if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then
    
    LC_ALL=C sed -i "/^\s*\$ActionSendStreamDriverMode /Id" "/etc/rsyslog.d/encrypt.conf"
else
    touch "/etc/rsyslog.d/encrypt.conf"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf"

cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak"
# Insert at the end of the file
printf '%s\n' "\$ActionSendStreamDriverMode 1" >> "/etc/rsyslog.d/encrypt.conf"
# Clean up after ourselves.
rm "/etc/rsyslog.d/encrypt.conf.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86098-1
  - DISA-STIG-RHEL-08-030710
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_actionsendstreamdrivermode

- name: Ensure Rsyslog Encrypts Off-Loaded Audit Records
  block:

  - name: Deduplicate values from /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: false
      regexp: '(?i)^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} '
      state: absent

  - name: Check if /etc/rsyslog.d exists
    ansible.builtin.stat:
      path: /etc/rsyslog.d
    register: _etc_rsyslog_d_exists

  - name: Check if the parameter $ActionSendStreamDriverMode is present in /etc/rsyslog.d
    ansible.builtin.find:
      paths: /etc/rsyslog.d
      recurse: 'yes'
      follow: 'no'
      contains: '^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} '
    register: _etc_rsyslog_d_has_parameter
    when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir

  - name: Remove parameter from files in /etc/rsyslog.d
    ansible.builtin.lineinfile:
      path: '{{ item.path }}'
      create: false
      regexp: '(?i)^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} '
      state: absent
    with_items: '{{ _etc_rsyslog_d_has_parameter.files }}'
    when: _etc_rsyslog_d_has_parameter.matched

  - name: Insert correct line to /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: true
      regexp: '(?i)^\s*{{ "$ActionSendStreamDriverMode"| regex_escape }} '
      line: $ActionSendStreamDriverMode 1
      state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-86098-1
  - DISA-STIG-RHEL-08-030710
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_actionsendstreamdrivermode
OVAL test results details

Check if $ActionSendStreamDriverMode 1 is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\$ActionSendStreamDriverMode 1$1

Check if StreamDriverMode is set to 1 in /etc/rsyslog.conf using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_rainer:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\s*action\(.*(?i)\btype\b(?-i)="omfwd".*(?i)\bStreamDriverMode\b(?-i)="1".*\)\s*$1

Check if $ActionSendStreamDriverMode 1 is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\$ActionSendStreamDriverMode 1$1

Check if StreamDriverMode is set to 1 in files in /etc/rsyslog.d using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_dir_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_actionsendstreamdrivermode_action_send_stream_driver_mode_rsyslog_dir_rainer:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\s*action\(.*(?i)\btype\b(?-i)="omfwd".*(?i)\bStreamDriverMode\b(?-i)="1".*\)\s*$1
Ensure Rsyslog Encrypts Off-Loaded Audit Recordsxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_defaultnetstreamdriver mediumCCE-85992-6

Ensure Rsyslog Encrypts Off-Loaded Audit Records

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_encrypt_offload_defaultnetstreamdriver
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_encrypt_offload_defaultnetstreamdriver:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-85992-6

References:
nistAU-4(1)
os-srgSRG-OS-000342-GPOS-00133, SRG-OS-000479-GPOS-00224
stigidRHEL-08-030710
stigrefSV-230481r958754_rule
Description
Rsyslogd is a system utility providing support for message logging. Support for both internet and UNIX domain sockets enables this utility to support both local and remote logging. Couple this utility with gnutls (which is a secure communications library implementing the SSL, TLS and DTLS protocols), and you have a method to securely encrypt and off-load auditing. When using rsyslogd to off-load logs off an encryption system must be used. Set the following configuration option in /etc/rsyslog.conf or in a file in /etc/rsyslog.d (using legacy syntax):
$DefaultNetstreamDriver gtls
Alternatively, use the RainerScript syntax:
global(DefaultNetstreamDriver="gtls")
Rationale
The audit records generated by Rsyslog contain valuable information regarding system configuration, user authentication, and other such information. Audit records should be protected from unauthorized access.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && rpm --quiet -q rsyslog; then

if [ -e "/etc/rsyslog.d/encrypt.conf" ] ; then
    
    LC_ALL=C sed -i "/^\s*\$DefaultNetstreamDriver /Id" "/etc/rsyslog.d/encrypt.conf"
else
    touch "/etc/rsyslog.d/encrypt.conf"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/rsyslog.d/encrypt.conf"

cp "/etc/rsyslog.d/encrypt.conf" "/etc/rsyslog.d/encrypt.conf.bak"
# Insert at the end of the file
printf '%s\n' "\$DefaultNetstreamDriver gtls" >> "/etc/rsyslog.d/encrypt.conf"
# Clean up after ourselves.
rm "/etc/rsyslog.d/encrypt.conf.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-85992-6
  - DISA-STIG-RHEL-08-030710
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_defaultnetstreamdriver

- name: Ensure Rsyslog Encrypts Off-Loaded Audit Records
  block:

  - name: Deduplicate values from /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: false
      regexp: '(?i)^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} '
      state: absent

  - name: Check if /etc/rsyslog.d exists
    ansible.builtin.stat:
      path: /etc/rsyslog.d
    register: _etc_rsyslog_d_exists

  - name: Check if the parameter $DefaultNetstreamDriver is present in /etc/rsyslog.d
    ansible.builtin.find:
      paths: /etc/rsyslog.d
      recurse: 'yes'
      follow: 'no'
      contains: '^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} '
    register: _etc_rsyslog_d_has_parameter
    when: _etc_rsyslog_d_exists.stat.isdir is defined and _etc_rsyslog_d_exists.stat.isdir

  - name: Remove parameter from files in /etc/rsyslog.d
    ansible.builtin.lineinfile:
      path: '{{ item.path }}'
      create: false
      regexp: '(?i)^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} '
      state: absent
    with_items: '{{ _etc_rsyslog_d_has_parameter.files }}'
    when: _etc_rsyslog_d_has_parameter.matched

  - name: Insert correct line to /etc/rsyslog.conf
    ansible.builtin.lineinfile:
      path: /etc/rsyslog.conf
      create: true
      regexp: '(?i)^\s*{{ "$DefaultNetstreamDriver"| regex_escape }} '
      line: $DefaultNetstreamDriver gtls
      state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-85992-6
  - DISA-STIG-RHEL-08-030710
  - NIST-800-53-AU-4(1)
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_encrypt_offload_defaultnetstreamdriver
OVAL test results details

Check if $DefaultNetstreamDriver gtls is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\$DefaultNetstreamDriver gtls$1

Check if DefaultNetstreamDriver is set to gtls in /etc/rsyslog.conf using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_rainer:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\s*global\(.*(?i)\bDefaultNetStreamDriver\b(?-i)="gtls".*\)\s*$1

Check if $DefaultNetstreamDriver gtls is set in /etc/rsyslog.conf  oval:ssg-test_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\$DefaultNetstreamDriver gtls$1

Check if DefaultNetstreamDriver is set to gtls in files in /etc/rsyslog.d using RainerScript  oval:ssg-test_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_dir_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rsyslog_encrypt_offload_defaultnetstreamdriver_default_netstream_rsyslog_dir_rainer:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.*conf$^\s*global\(.*(?i)\bDefaultNetStreamDriver\b(?-i)="gtls".*\)\s*$1
Ensure remote access methods are monitored in Rsyslogxccdf_org.ssgproject.content_rule_rsyslog_remote_access_monitoring mediumCCE-83426-7

Ensure remote access methods are monitored in Rsyslog

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_remote_access_monitoring
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_remote_access_monitoring:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-83426-7

References:
nistAC-17(1)
os-srgSRG-OS-000032-GPOS-00013
stigidRHEL-08-010070
stigrefSV-230228r1069299_rule
Description
Logging of remote access methods must be implemented to help identify cyber attacks and ensure ongoing compliance with remote access policies are being audited and upheld. An examples of a remote access method is the use of the Remote Desktop Protocol (RDP) from an external, non-organization controlled network. The /etc/rsyslog.conf or /etc/rsyslog.d/*.conf file should contain a match for the following selectors: auth.*, authpriv.*, and daemon.*. If not, use the following as an example configuration: auth.*;authpriv.* /var/log/secure daemon.* /var/log/messages
Rationale
Logging remote access methods can be used to trace the decrease the risks associated with remote user access management. It can also be used to spot cyber attacks and ensure ongoing compliance with organizational policies surrounding the use of remote access methods.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && rpm --quiet -q rsyslog; then

declare -A REMOTE_METHODS=( ['auth.*']='^[^#]*auth\.\*.*$' ['authpriv.*']='^[^#]*authpriv\.\*.*$' ['daemon.*']='^[^#]*daemon\.\*.*$' )
declare -A LOCATIONS=( ['auth.*']='/var/log/secure' ['authpriv.*']='/var/log/secure' ['daemon.*']='/var/log/messages' )

if [[ ! -f /etc/rsyslog.conf ]]; then
	# Something is not right, create the file
	touch /etc/rsyslog.conf
fi


# Loop through the remote methods associative array
for K in "${!REMOTE_METHODS[@]}"
do
	# Check to see if selector/value exists
	if ! grep -rq "${REMOTE_METHODS[$K]}" /etc/rsyslog.*; then
        APPEND_LINE=$(sed -rn "/^\S+\s+\${LOCATIONS[$K]}$/p" /etc/rsyslog.conf)
		# Make sure we have a line to insert after, otherwise append to end
		if [[ ! -z ${APPEND_LINE} ]]; then
			# Add selector to file
			sed -r -i "0,/^(\S+\s+\/var\/log\/secure$)/s//\1\n${K} \/var\/log\/secure/" /etc/rsyslog.conf
		else
			echo "${K} ${LOCATIONS[$K]}" >> /etc/rsyslog.conf
		fi
	fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Set facts'
  ansible.builtin.set_fact:
    conf_files:
    - /etc/rsyslog.conf
    remote_methods:
    - selector: auth.*
      regexp: ^.*auth\.\*.*$
      location: /var/log/secure
    - selector: authpriv.*
      regexp: ^.*authpriv\.\*.*$
      location: /var/log/secure
    - selector: daemon.*
      regexp: ^.*daemon\.\*.*$
      location: /var/log/messages
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Ensure rsyslog.conf
    exists'
  ansible.builtin.file:
    path: '{{ conf_files.0 }}'
    state: touch
    access_time: preserve
    modification_time: preserve
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Gather conf.d files'
  ansible.builtin.find:
    patterns:
    - '*.conf'
    paths:
    - /etc/rsyslog.d
  register: rsyslogd
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Set conf file(s)'
  ansible.builtin.set_fact:
    conf_files: '{{ conf_files + [item.path] }}'
  loop: '{{ rsyslogd.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  - rsyslogd.matched > 0
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Check for existing
    values'
  ansible.builtin.lineinfile:
    path: '{{ item.1 }}'
    regexp: '{{ item.0.regexp }}'
    state: absent
  check_mode: true
  changed_when: false
  register: remote_method_values
  loop: '{{ remote_methods|product(conf_files)|list }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring

- name: 'Ensure remote access methods are monitored in Rsyslog: Configure'
  ansible.builtin.lineinfile:
    path: /etc/rsyslog.conf
    line: '{{ item.item.0.selector }} {{ item.item.0.location }}'
    insertafter: ^.*\/var\/log\/secure.*$
    create: true
  loop: '{{ remote_method_values.results }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"rsyslog" in ansible_facts.packages'
  - item.found == 0
  tags:
  - CCE-83426-7
  - DISA-STIG-RHEL-08-010070
  - NIST-800-53-AC-17(1)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - rsyslog_remote_access_monitoring
OVAL test results details

remote method auth monitoring configured in rsyslog'  oval:ssg-test_remote_method_monitoring_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_remote_method_monitoring_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/rsyslog\.(conf|d/.+\.conf)$^[^#\n]*auth(,\w+)*\.\*[^\n]*$1

remote method authpriv monitoring configured in rsyslog'  oval:ssg-test_remote_method_monitoring_authpriv:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/rsyslog.confauthpriv.* /var/log/secure

remote method daemon monitoring configured in rsyslog'  oval:ssg-test_remote_method_monitoring_daemon:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_remote_method_monitoring_daemon:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/rsyslog\.(conf|d/.+\.conf)$^[^#\n]*daemon(,\w+)*\.\*[^\n]*$1
Ensure Logs Sent To Remote Hostxccdf_org.ssgproject.content_rule_rsyslog_remote_loghost mediumCCE-80863-4

Ensure Logs Sent To Remote Host

Rule IDxccdf_org.ssgproject.content_rule_rsyslog_remote_loghost
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rsyslog_remote_loghost:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-80863-4

References:
cis-csc1, 13, 14, 15, 16, 2, 3, 5, 6
cobit5APO11.04, APO13.01, BAI03.05, BAI04.04, DSS05.04, DSS05.07, MEA02.01
hipaa164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(B), 164.308(a)(5)(ii)(C), 164.308(a)(6)(ii), 164.308(a)(8), 164.310(d)(2)(iii), 164.312(b), 164.314(a)(2)(i)(C), 164.314(a)(2)(iii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 7.1, SR 7.2
ism0988, 1405
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.17.2.1
nerc-cipCIP-003-8 R5.2, CIP-004-6 R3.3
nistCM-6(a), AU-4(1), AU-9(2)
nist-csfPR.DS-4, PR.PT-1
os-srgSRG-OS-000479-GPOS-00224, SRG-OS-000480-GPOS-00227, SRG-OS-000342-GPOS-00133
anssiR71
stigidRHEL-08-030690
stigrefSV-230479r958754_rule
Description
To configure rsyslog to send logs to a remote log server, open /etc/rsyslog.conf and read and understand the last section of the file, which describes the multiple directives necessary to activate remote logging. Along with these other directives, the system can be configured to forward its logs to a particular log server by adding or correcting one of the following lines, substituting logcollector appropriately. The choice of protocol depends on the environment of the system; although TCP and RELP provide more reliable message delivery, they may not be supported in all environments.
To use UDP for log message delivery:
*.* @logcollector

Or in RainerScript:
*.* action(type="omfwd" ... target="logcollector" protocol="udp")

To use TCP for log message delivery:
*.* @@logcollector

Or in RainerScript:
*.* action(type="omfwd" ... target="logcollector" protocol="tcp")

To use RELP for log message delivery:
*.* :omrelp:logcollector

Or in RainerScript:
*.* action(type="omfwd" ... target="logcollector" protocol="relp")

There must be a resolvable DNS CNAME or Alias record set to "logcollector" for logs to be sent correctly to the centralized logging utility.
Rationale
A log server (loghost) receives syslog messages from one or more systems. This data can be used as an additional log source in the event a system is compromised and its local logs are suspect. Forwarding log messages to a remote loghost also provides system administrators with a centralized place to view the status of multiple hosts within the enterprise.
Warnings
warning  It is important to configure queues in case the client is sending log messages to a remote server. If queues are not configured, the system will stop functioning when the connection to the remote server is not available. Please consult Rsyslog documentation for more information about configuration of queues. The example configuration which should go into /etc/rsyslog.conf can look like the following lines:
$ActionQueueType LinkedList
$ActionQueueFileName queuefilename
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionResumeRetryCount -1
Or if using Rainer Script syntax, it could be:
*.* action(type="omfwd" queue.type="linkedlist" queue.filename="example_fwd" action.resumeRetryCount="-1" queue.saveOnShutdown="on" target="example.com" port="30514" protocol="tcp")

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

rsyslog_remote_loghost_address='logcollector'


# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^\*\.\*")

# shellcheck disable=SC2059
printf -v formatted_output "%s %s" "$stripped_key" "@@$rsyslog_remote_loghost_address"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^\*\.\*\\>" "/etc/rsyslog.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^\*\.\*\\>.*/$escaped_formatted_output/gi" "/etc/rsyslog.conf"
else
    if [[ -s "/etc/rsyslog.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/rsyslog.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/rsyslog.conf"
    fi
    cce="CCE-80863-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/rsyslog.conf" >> "/etc/rsyslog.conf"
    printf '%s\n' "$formatted_output" >> "/etc/rsyslog.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80863-4
  - DISA-STIG-RHEL-08-030690
  - NIST-800-53-AU-4(1)
  - NIST-800-53-AU-9(2)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - rsyslog_remote_loghost
- name: XCCDF Value rsyslog_remote_loghost_address # promote to variable
  set_fact:
    rsyslog_remote_loghost_address: !!str logcollector
  tags:
    - always

- name: Set rsyslog remote loghost
  ansible.builtin.lineinfile:
    dest: /etc/rsyslog.conf
    regexp: ^\*\.\*
    line: '*.* @@{{ rsyslog_remote_loghost_address }}'
    create: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80863-4
  - DISA-STIG-RHEL-08-030690
  - NIST-800-53-AU-4(1)
  - NIST-800-53-AU-9(2)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - rsyslog_remote_loghost
OVAL test results details

Ensures system configured to export logs to remote host  oval:ssg-test_remote_rsyslog_conf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_remote_loghost_rsyslog_conf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf^\*\.\*[\s]+(?:@|\:omrelp\:)1

Ensures system configured to export logs to remote host  oval:ssg-test_remote_rsyslog_d:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_remote_loghost_rsyslog_d:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.+\.conf$^\*\.\*[\s]+(?:@|\:omrelp\:)1

Ensures system configured to export logs to remote host using Rainer syntax  oval:ssg-test_remote_rsyslog_conf_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_remote_loghost_rsyslog_conf_rainer:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/rsyslog.conf(?m)^\s*\*\.\*\s+action\(\s*.*(?i)\btype\b(?-i)="omfwd"\s*.*(?i)\btarget\b(?-i)="\S+"\s*.*\)\s*$1

Ensures system configured to export logs to remote host using Rainer  oval:ssg-test_remote_rsyslog_d_rainer:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_remote_loghost_rsyslog_d_rainer:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/rsyslog.d^.+\.conf$(?m)^\s*\*\.\*\s+action\(\s*.*(?i)\btype\b(?-i)="omfwd"\s*.*(?i)\btarget\b(?-i)="\S+"\s*.*\)\s*$1
Ensure rsyslog-gnutls is installedxccdf_org.ssgproject.content_rule_package_rsyslog-gnutls_installed mediumCCE-82859-0

Ensure rsyslog-gnutls is installed

Rule IDxccdf_org.ssgproject.content_rule_package_rsyslog-gnutls_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_rsyslog-gnutls_installed:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-82859-0

References:
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000120-GPOS-00061
anssiR71
stigidRHEL-08-030680
stigrefSV-230478r1017268_rule
Description
TLS protocol support for rsyslog is installed. The rsyslog-gnutls package can be installed with the following command:
$ sudo yum install rsyslog-gnutls
Rationale
The rsyslog-gnutls package provides Transport Layer Security (TLS) support for the rsyslog daemon, which enables secure remote logging.
OVAL test results details

package rsyslog-gnutls is installed  oval:ssg-test_package_rsyslog-gnutls_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedrsyslog-gnutlsx86_64(none)15.el88.2102.00:8.2102.0-15.el8199e2f91fd431d51rsyslog-gnutls-0:8.2102.0-15.el8.x86_64
Ensure rsyslog is Installedxccdf_org.ssgproject.content_rule_package_rsyslog_installed mediumCCE-80847-7

Ensure rsyslog is Installed

Rule IDxccdf_org.ssgproject.content_rule_package_rsyslog_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_rsyslog_installed:def:1
Time2025-11-18T17:05:26-05:00
Severitymedium
Identifiers:

CCE-80847-7

References:
cis-csc1, 14, 15, 16, 3, 5, 6
cobit5APO11.04, BAI03.05, DSS05.04, DSS05.07, MEA02.01
hipaa164.312(a)(2)(ii)
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1
nistCM-6(a)
nist-csfPR.PT-1
os-srgSRG-OS-000479-GPOS-00224, SRG-OS-000051-GPOS-00024, SRG-OS-000480-GPOS-00227
cis5.1.1.1
stigidRHEL-08-030670
stigrefSV-230477r1017267_rule
Description
Rsyslog is installed by default. The rsyslog package can be installed with the following command:
 $ sudo yum install rsyslog
Rationale
The rsyslog package provides the rsyslog daemon, which provides system logging services.
OVAL test results details

package rsyslog is installed  oval:ssg-test_package_rsyslog_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedrsyslogx86_64(none)15.el88.2102.00:8.2102.0-15.el8199e2f91fd431d51rsyslog-0:8.2102.0-15.el8.x86_64
Enable rsyslog Servicexccdf_org.ssgproject.content_rule_service_rsyslog_enabled mediumCCE-80886-5

Enable rsyslog Service

Rule IDxccdf_org.ssgproject.content_rule_service_rsyslog_enabled
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-service_rsyslog_enabled:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-80886-5

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO13.01, BAI03.05, BAI04.04, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
hipaa164.312(a)(2)(ii)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2, A.17.2.1
nistCM-6(a), AU-4(1)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.DS-4, PR.PT-1
os-srgSRG-OS-000480-GPOS-00227
cis5.1.1.2
stigidRHEL-08-010561
stigrefSV-230298r1017108_rule
Description
The rsyslog service provides syslog-style logging by default on Red Hat Enterprise Linux 8. The rsyslog service can be enabled with the following command:
$ sudo systemctl enable rsyslog.service
Rationale
The rsyslog service must be running in order to provide logging services, which are essential to system administration.
OVAL test results details

package rsyslog is installed  oval:ssg-test_service_rsyslog_package_rsyslog_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedrsyslogx86_64(none)15.el88.2102.00:8.2102.0-15.el8199e2f91fd431d51rsyslog-0:8.2102.0-15.el8.x86_64

Test that the rsyslog service is running  oval:ssg-test_service_running_rsyslog:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
truersyslog.serviceActiveStateactive

systemd test  oval:ssg-test_multi_user_wants_rsyslog:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
truemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_rsyslog_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Install firewalld Packagexccdf_org.ssgproject.content_rule_package_firewalld_installed mediumCCE-82998-6

Install firewalld Package

Rule IDxccdf_org.ssgproject.content_rule_package_firewalld_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_firewalld_installed:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-82998-6

References:
nistCM-6(a)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000297-GPOS-00115, SRG-OS-000298-GPOS-00116, SRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00232
cis3.4.1.2
pcidss41.2.1, 1.2
stigidRHEL-08-040100
stigrefSV-230505r958672_rule
Description
The firewalld package can be installed with the following command:
$ sudo yum install firewalld
Rationale
"Firewalld" provides an easy and effective way to block/limit remote access to the system via ports, services, and protocols. Remote access services, such as those providing remote access to network devices and information systems, which lack automated control capabilities, increase risk and make remote user access management difficult at best. Remote access is access to nonpublic information systems by an authorized user (or an information system) communicating through an external, non-organization-controlled network. Remote access methods include, for example, dial-up, broadband, and wireless. Red Hat Enterprise Linux 8 functionality (e.g., SSH) must be capable of taking enforcement action if the audit reveals unauthorized activity. Automated control of remote access sessions allows organizations to ensure ongoing compliance with remote access policies by enforcing connection rules of remote access applications on a variety of information system components (e.g., servers, workstations, notebook computers, smartphones, and tablets)."
OVAL test results details

package firewalld is installed  oval:ssg-test_package_firewalld_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedfirewalldnoarch(none)4.el80.9.110:0.9.11-4.el8199e2f91fd431d51firewalld-0:0.9.11-4.el8.noarch
Verify firewalld Enabledxccdf_org.ssgproject.content_rule_service_firewalld_enabled mediumCCE-80877-4

Verify firewalld Enabled

Rule IDxccdf_org.ssgproject.content_rule_service_firewalld_enabled
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-service_firewalld_enabled:def:1
Time2025-11-18T17:05:36-05:00
Severitymedium
Identifiers:

CCE-80877-4

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.3, 3.4.7
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nerc-cipCIP-003-8 R4, CIP-003-8 R5, CIP-004-6 R3
nistAC-4, CM-7(b), CA-3(5), SC-7(21), CM-6(a)
nist-csfPR.IP-1
osppFMT_SMF_EXT.1
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000297-GPOS-00115, SRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00231, SRG-OS-000480-GPOS-00232
bsiSYS.1.6.A5, SYS.1.6.A21
cis3.4.1.2
pcidss41.2.1, 1.2
stigidRHEL-08-040101
stigrefSV-244544r958672_rule
Description
The firewalld service can be enabled with the following command:
$ sudo systemctl enable firewalld.service
Rationale
Access control methods provide the ability to enhance system security posture by restricting services and known good IP addresses and address ranges. This prevents connections from unknown hosts and protocols.
OVAL test results details

package firewalld is installed  oval:ssg-test_service_firewalld_package_firewalld_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedfirewalldnoarch(none)4.el80.9.110:0.9.11-4.el8199e2f91fd431d51firewalld-0:0.9.11-4.el8.noarch

Test that the firewalld service is running  oval:ssg-test_service_running_firewalld:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
truefirewalld.serviceActiveStateactive

systemd test  oval:ssg-test_multi_user_wants_firewalld:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
truemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_firewalld_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Configure the Firewalld Portsxccdf_org.ssgproject.content_rule_configure_firewalld_ports mediumCCE-84300-3

Configure the Firewalld Ports

Rule IDxccdf_org.ssgproject.content_rule_configure_firewalld_ports
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:05:36-05:00
Severitymedium
Identifiers:

CCE-84300-3

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
ism1416
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistAC-4, CM-7(b), CA-3(5), SC-7(21), CM-6(a)
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000297-GPOS-00115
pcidss41.3.1, 1.3
stigidRHEL-08-040030
stigrefSV-230500r1101900_rule
Description
Configure the firewalld ports to allow approved services to have access to the system. To configure firewalld to open ports, run the following command:
firewall-cmd --permanent --add-port=port_number/tcp
To configure firewalld to allow access for pre-defined services, run the following command:
firewall-cmd --permanent --add-service=service_name
Rationale
In order to prevent unauthorized connection of devices, unauthorized transfer of information, or unauthorized tunneling (i.e., embedding of data types within data types), organizations must disable or restrict unused or unnecessary physical and logical ports/protocols on information systems.

Operating systems are capable of providing a wide variety of functions and services. Some of the functions and services provided by default may not be necessary to support essential organizational operations. Additionally, it is sometimes convenient to provide multiple services from a single component (e.g., VPN and IPS); however, doing so increases risk over limiting the services provided by one component.

To support the requirements and principles of least functionality, the operating system must support the organizational requirements, providing only essential capabilities and limiting the use of ports, protocols, and/or services to only those required, authorized, and approved to conduct official business.
Evaluation messages
info 
No candidate or applicable check found.
Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systemsxccdf_org.ssgproject.content_rule_configured_firewalld_default_deny mediumCCE-86266-4

Firewalld Must Employ a Deny-all, Allow-by-exception Policy for Allowing Connections to Other Systems

Rule IDxccdf_org.ssgproject.content_rule_configured_firewalld_default_deny
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:05:36-05:00
Severitymedium
Identifiers:

CCE-86266-4

References:
nistAC-17 (1)
os-srgSRG-OS-000297-GPOS-00115
stigidRHEL-08-040090
stigrefSV-230504r958672_rule
Description
Red Hat Enterprise Linux 8 incorporates the "firewalld" daemon, which allows for many different configurations. One of these configurations is zones. Zones can be utilized to a deny-all, allow-by-exception approach. The default "drop" zone will drop all incoming network packets unless it is explicitly allowed by the configuration file or is related to an outgoing network connection.
Rationale
Failure to restrict network connectivity only to authorized systems permits inbound connections from malicious systems. It also permits outbound connections that may facilitate exfiltration of data.
Evaluation messages
info 
No candidate or applicable check found.
Set Default firewalld Zone for Incoming Packetsxccdf_org.ssgproject.content_rule_set_firewalld_default_zone mediumCCE-80890-7

Set Default firewalld Zone for Incoming Packets

Rule IDxccdf_org.ssgproject.content_rule_set_firewalld_default_zone
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-set_firewalld_default_zone:def:1
Time2025-11-18T17:05:36-05:00
Severitymedium
Identifiers:

CCE-80890-7

References:
cis-csc11, 14, 3, 9
cjis5.10.1
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.3, 3.4.7, 3.13.6
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
ism1416
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCA-3(5), CM-7(b), SC-7(23), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
pcidssReq-1.4
os-srgSRG-OS-000480-GPOS-00227
pcidss41.3.1, 1.3
stigidRHEL-08-040090
stigrefSV-230504r958672_rule
Description
To set the default zone to drop for the built-in default zone which processes incoming IPv4 and IPv6 packets, modify the following line in /etc/firewalld/firewalld.conf to be:
DefaultZone=drop
Rationale
In firewalld the default zone is applied only after all the applicable rules in the table are examined for a match. Setting the default zone to drop implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.
Warnings
warning  To prevent denying any access to the system, automatic remediation of this control is not available. Remediation must be automated as a component of machine provisioning, or followed manually as outlined above.
OVAL test results details

tests the presence of 'DefaultZone=drop' setting in the /etc/firewalld/firewalld.conf file  oval:ssg-test_set_firewalld_default_zone:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_set_firewalld_default_zone:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/firewalld/firewalld.conf^[\s]*DefaultZone=drop[\s]*$1
Configure Firewalld to Use the Nftables Backendxccdf_org.ssgproject.content_rule_firewalld-backend mediumCCE-86506-3

Configure Firewalld to Use the Nftables Backend

Rule IDxccdf_org.ssgproject.content_rule_firewalld-backend
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-firewalld-backend:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-86506-3

References:
nistSC-5
os-srgSRG-OS-000420-GPOS-00186
stigidRHEL-08-040150
stigrefSV-230525r958902_rule
Description
Firewalld can be configured with many backends, such as nftables.
Rationale
Nftables is modern kernel module for controling network connections coming into a system. Utilizing the limit statement in "nftables" can help to mitigate DoS attacks.
OVAL test results details

tests the value of FirewallBackend setting in the /etc/firewalld/firewalld.conf file  oval:ssg-test_firewalld-backend:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/firewalld/firewalld.confFirewallBackend=nftables
Configure Accepting Router Advertisements on All IPv6 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_ra mediumCCE-81006-9

Configure Accepting Router Advertisements on All IPv6 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_ra
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_all_accept_ra:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81006-9

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.20
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
cis3.3.11
stigidRHEL-08-040261
stigrefSV-230541r1017303_rule
Description
To set the runtime status of the net.ipv6.conf.all.accept_ra kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.all.accept_ra=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.all.accept_ra = 0
Rationale
An illicit router advertisement message could result in a man-in-the-middle attack.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.all.accept_ra from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_ra.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.all.accept_ra" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_all_accept_ra_value='0'


#
# Set runtime for net.ipv6.conf.all.accept_ra
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.all.accept_ra="$sysctl_net_ipv6_conf_all_accept_ra_value"
fi

#
# If net.ipv6.conf.all.accept_ra present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.all.accept_ra = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_ra")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_ra_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_ra\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.all.accept_ra\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81006-9"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81006-9
  - DISA-STIG-RHEL-08-040261
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_ra

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.all.accept_ra.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81006-9
  - DISA-STIG-RHEL-08-040261
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_ra

- name: Comment out any occurrences of net.ipv6.conf.all.accept_ra from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.all.accept_ra
    replace: '#net.ipv6.conf.all.accept_ra'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81006-9
  - DISA-STIG-RHEL-08-040261
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_ra
- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_ra_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_all_accept_ra_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.all.accept_ra is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.all.accept_ra
    value: '{{ sysctl_net_ipv6_conf_all_accept_ra_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81006-9
  - DISA-STIG-RHEL-08-040261
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_ra

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.all.accept_ra%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_ra.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.all.accept_ra static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_ra_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_ra:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1

net.ipv6.conf.all.accept_ra static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_ra_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_ra:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_ra:obj:1

net.ipv6.conf.all.accept_ra static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_ra_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_accept_ra:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.accept_ra[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.accept_ra set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_ra_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.accept_ra1
Disable Accepting ICMP Redirects for All IPv6 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_redirects mediumCCE-81009-3

Disable Accepting ICMP Redirects for All IPv6 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_all_accept_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81009-3

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.20
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a), CM-6(b), CM-6.1(iv)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
anssiR13
cis3.3.5
stigidRHEL-08-040280
stigrefSV-230544r1017306_rule
Description
To set the runtime status of the net.ipv6.conf.all.accept_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.all.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.all.accept_redirects = 0
Rationale
An illicit ICMP redirect message could result in a man-in-the-middle attack.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.all.accept_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.all.accept_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_all_accept_redirects_value='0'


#
# Set runtime for net.ipv6.conf.all.accept_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.all.accept_redirects="$sysctl_net_ipv6_conf_all_accept_redirects_value"
fi

#
# If net.ipv6.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.all.accept_redirects = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_redirects_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81009-3"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81009-3
  - DISA-STIG-RHEL-08-040280
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.all.accept_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81009-3
  - DISA-STIG-RHEL-08-040280
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_redirects

- name: Comment out any occurrences of net.ipv6.conf.all.accept_redirects from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.all.accept_redirects
    replace: '#net.ipv6.conf.all.accept_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81009-3
  - DISA-STIG-RHEL-08-040280
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_redirects
- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_all_accept_redirects_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.all.accept_redirects is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.all.accept_redirects
    value: '{{ sysctl_net_ipv6_conf_all_accept_redirects_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81009-3
  - DISA-STIG-RHEL-08-040280
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.all.accept_redirects%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_redirects.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.all.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1

net.ipv6.conf.all.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_redirects:obj:1

net.ipv6.conf.all.accept_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.accept_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.accept_redirects set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_redirects_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.accept_redirects1
Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_source_route mediumCCE-81013-5

Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv6 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_accept_source_route
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_all_accept_source_route:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81013-5

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 4, 6, 8, 9
cobit5APO01.06, APO13.01, DSS01.05, DSS03.01, DSS05.02, DSS05.04, DSS05.07, DSS06.02
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.4.3.3
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfDE.AE-1, ID.AM-3, PR.AC-5, PR.DS-5, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR13
cis3.3.8
stigidRHEL-08-040240
stigrefSV-230538r1017300_rule
Description
To set the runtime status of the net.ipv6.conf.all.accept_source_route kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.all.accept_source_route=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.all.accept_source_route = 0
Rationale
Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures. This requirement applies only to the forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and the system is functioning as a router.

Accepting source-routed packets in the IPv6 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.all.accept_source_route from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.accept_source_route.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.all.accept_source_route" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_all_accept_source_route_value='0'


#
# Set runtime for net.ipv6.conf.all.accept_source_route
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.all.accept_source_route="$sysctl_net_ipv6_conf_all_accept_source_route_value"
fi

#
# If net.ipv6.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.all.accept_source_route = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.accept_source_route")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_accept_source_route_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.accept_source_route\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.all.accept_source_route\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81013-5"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81013-5
  - DISA-STIG-RHEL-08-040240
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_source_route

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.all.accept_source_route.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81013-5
  - DISA-STIG-RHEL-08-040240
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_source_route

- name: Comment out any occurrences of net.ipv6.conf.all.accept_source_route from
    config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.all.accept_source_route
    replace: '#net.ipv6.conf.all.accept_source_route'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81013-5
  - DISA-STIG-RHEL-08-040240
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_source_route
- name: XCCDF Value sysctl_net_ipv6_conf_all_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_all_accept_source_route_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.all.accept_source_route is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.all.accept_source_route
    value: '{{ sysctl_net_ipv6_conf_all_accept_source_route_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81013-5
  - DISA-STIG-RHEL-08-040240
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_accept_source_route

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.all.accept_source_route%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_all_accept_source_route.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.all.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_source_route_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1

net.ipv6.conf.all.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_source_route_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_accept_source_route:obj:1

net.ipv6.conf.all.accept_source_route static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_source_route_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_accept_source_route:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.accept_source_route[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.accept_source_route set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_all_accept_source_route_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv6.conf.all.accept_source_route0
Disable Kernel Parameter for IPv6 Forwardingxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_forwarding mediumCCE-82863-2

Disable Kernel Parameter for IPv6 Forwarding

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_all_forwarding
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_all_forwarding:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-82863-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 2, 3, 7, 8, 9
cobit5APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS05.02, DSS05.05, DSS05.07, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a), CM-6(b), CM-6.1(iv)
nist-csfDE.CM-1, PR.DS-4, PR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
cis3.3.1
stigidRHEL-08-040260
stigrefSV-230540r1017302_rule
Description
To set the runtime status of the net.ipv6.conf.all.forwarding kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.all.forwarding=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.all.forwarding = 0
Rationale
IP forwarding permits the kernel to forward packets from one network interface to another. The ability to forward packets between two networks is only appropriate for systems acting as routers.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.all.forwarding from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.all.forwarding.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.all.forwarding" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_all_forwarding_value='0'


#
# Set runtime for net.ipv6.conf.all.forwarding
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.all.forwarding="$sysctl_net_ipv6_conf_all_forwarding_value"
fi

#
# If net.ipv6.conf.all.forwarding present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.all.forwarding = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.all.forwarding")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_all_forwarding_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.all.forwarding\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.all.forwarding\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-82863-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82863-2
  - DISA-STIG-RHEL-08-040260
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_forwarding

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.all.forwarding.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82863-2
  - DISA-STIG-RHEL-08-040260
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_forwarding

- name: Comment out any occurrences of net.ipv6.conf.all.forwarding from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.all.forwarding
    replace: '#net.ipv6.conf.all.forwarding'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82863-2
  - DISA-STIG-RHEL-08-040260
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_forwarding
- name: XCCDF Value sysctl_net_ipv6_conf_all_forwarding_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_all_forwarding_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.all.forwarding is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.all.forwarding
    value: '{{ sysctl_net_ipv6_conf_all_forwarding_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82863-2
  - DISA-STIG-RHEL-08-040260
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_all_forwarding
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.all.forwarding static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_forwarding_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_forwarding:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1

net.ipv6.conf.all.forwarding static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_forwarding_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_forwarding:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_forwarding:obj:1

net.ipv6.conf.all.forwarding static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_forwarding_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_forwarding:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.forwarding[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.forwarding set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_all_forwarding_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv6.conf.all.forwarding0
Disable Accepting Router Advertisements on all IPv6 Interfaces by Defaultxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_ra mediumCCE-81007-7

Disable Accepting Router Advertisements on all IPv6 Interfaces by Default

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_ra
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_default_accept_ra:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81007-7

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.20
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
cis3.3.11
stigidRHEL-08-040262
stigrefSV-230542r1017304_rule
Description
To set the runtime status of the net.ipv6.conf.default.accept_ra kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.default.accept_ra=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.default.accept_ra = 0
Rationale
An illicit router advertisement message could result in a man-in-the-middle attack.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.default.accept_ra from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_ra.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.default.accept_ra" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_default_accept_ra_value='0'


#
# Set runtime for net.ipv6.conf.default.accept_ra
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.default.accept_ra="$sysctl_net_ipv6_conf_default_accept_ra_value"
fi

#
# If net.ipv6.conf.default.accept_ra present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.default.accept_ra = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_ra")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_ra_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_ra\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.default.accept_ra\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81007-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81007-7
  - DISA-STIG-RHEL-08-040262
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_ra

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.default.accept_ra.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81007-7
  - DISA-STIG-RHEL-08-040262
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_ra

- name: Comment out any occurrences of net.ipv6.conf.default.accept_ra from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.default.accept_ra
    replace: '#net.ipv6.conf.default.accept_ra'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81007-7
  - DISA-STIG-RHEL-08-040262
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_ra
- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_ra_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_default_accept_ra_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.default.accept_ra is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.default.accept_ra
    value: '{{ sysctl_net_ipv6_conf_default_accept_ra_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81007-7
  - DISA-STIG-RHEL-08-040262
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_ra

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.default.accept_ra%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_ra.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.default.accept_ra static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_ra_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_ra:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1

net.ipv6.conf.default.accept_ra static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_ra_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_ra:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_ra:obj:1

net.ipv6.conf.default.accept_ra static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_ra_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_default_accept_ra:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.default.accept_ra[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.default.accept_ra set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_ra_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.default.accept_ra1
Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_redirects mediumCCE-81010-1

Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv6 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_default_accept_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81010-1

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.1.20
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
anssiR13
cis3.3.5
stigidRHEL-08-040210
stigrefSV-230535r1017297_rule
Description
To set the runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.default.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.default.accept_redirects = 0
Rationale
An illicit ICMP redirect message could result in a man-in-the-middle attack.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.default.accept_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.default.accept_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_default_accept_redirects_value='0'


#
# Set runtime for net.ipv6.conf.default.accept_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.default.accept_redirects="$sysctl_net_ipv6_conf_default_accept_redirects_value"
fi

#
# If net.ipv6.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.default.accept_redirects = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_redirects_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81010-1"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81010-1
  - DISA-STIG-RHEL-08-040210
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.default.accept_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81010-1
  - DISA-STIG-RHEL-08-040210
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_redirects

- name: Comment out any occurrences of net.ipv6.conf.default.accept_redirects from
    config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.default.accept_redirects
    replace: '#net.ipv6.conf.default.accept_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81010-1
  - DISA-STIG-RHEL-08-040210
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_redirects
- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_default_accept_redirects_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.default.accept_redirects is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.default.accept_redirects
    value: '{{ sysctl_net_ipv6_conf_default_accept_redirects_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81010-1
  - DISA-STIG-RHEL-08-040210
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.default.accept_redirects%20%3D%200%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_redirects.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.default.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1

net.ipv6.conf.default.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_redirects:obj:1

net.ipv6.conf.default.accept_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.default.accept_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.default.accept_redirects set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_redirects_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.default.accept_redirects1
Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Defaultxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_source_route mediumCCE-81015-0

Disable Kernel Parameter for Accepting Source-Routed Packets on IPv6 Interfaces by Default

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_source_route
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv6_conf_default_accept_source_route:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81015-0

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 4, 6, 8, 9
cobit5APO01.06, APO13.01, DSS01.05, DSS03.01, DSS05.02, DSS05.04, DSS05.07, DSS06.02
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.4.3.3
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-7(a), CM-7(b), CM-6(a), CM-6(b), CM-6.1(iv)
nist-csfDE.AE-1, ID.AM-3, PR.AC-5, PR.DS-5, PR.PT-4
pcidssReq-1.4.3
os-srgSRG-OS-000480-GPOS-00227
anssiR13
cis3.3.8
pcidss41.4.2, 1.4
stigidRHEL-08-040250
stigrefSV-230539r1017301_rule
Description
To set the runtime status of the net.ipv6.conf.default.accept_source_route kernel parameter, run the following command:
$ sudo sysctl -w net.ipv6.conf.default.accept_source_route=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv6.conf.default.accept_source_route = 0
Rationale
Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures. This requirement applies only to the forwarding of source-routerd traffic, such as when IPv6 forwarding is enabled and the system is functioning as a router. Accepting source-routed packets in the IPv6 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv6.conf.default.accept_source_route from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_source_route.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv6.conf.default.accept_source_route" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv6_conf_default_accept_source_route_value='0'


#
# Set runtime for net.ipv6.conf.default.accept_source_route
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv6.conf.default.accept_source_route="$sysctl_net_ipv6_conf_default_accept_source_route_value"
fi

#
# If net.ipv6.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.default.accept_source_route = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_source_route")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_source_route_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_source_route\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81015-0"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81015-0
  - DISA-STIG-RHEL-08-040250
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_source_route

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv6.conf.default.accept_source_route.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81015-0
  - DISA-STIG-RHEL-08-040250
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_source_route

- name: Comment out any occurrences of net.ipv6.conf.default.accept_source_route from
    config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv6.conf.default.accept_source_route
    replace: '#net.ipv6.conf.default.accept_source_route'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81015-0
  - DISA-STIG-RHEL-08-040250
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_source_route
- name: XCCDF Value sysctl_net_ipv6_conf_default_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv6_conf_default_accept_source_route_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv6.conf.default.accept_source_route is set
  ansible.posix.sysctl:
    name: net.ipv6.conf.default.accept_source_route
    value: '{{ sysctl_net_ipv6_conf_default_accept_source_route_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81015-0
  - DISA-STIG-RHEL-08-040250
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(b)
  - NIST-800-53-CM-6.1(iv)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv6_conf_default_accept_source_route

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv6.conf.default.accept_source_route%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv6_conf_default_accept_source_route.conf
        overwrite: true
OVAL test results details

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1

net.ipv6.conf.all.disable_ipv6 static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_all_disable_ipv6:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.all.disable_ipv6[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.all.disable_ipv6 set to 1  oval:ssg-test_sysctl_net_ipv6_conf_all_disable_ipv6_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv6.conf.all.disable_ipv60

net.ipv6.conf.default.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_source_route_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1

net.ipv6.conf.default.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_source_route_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv6_conf_default_accept_source_route:obj:1

net.ipv6.conf.default.accept_source_route static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_source_route_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv6_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv6.conf.default.accept_source_route[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv6.conf.default.accept_source_route set to the appropriate value  oval:ssg-test_sysctl_net_ipv6_conf_default_accept_source_route_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv6.conf.default.accept_source_route0
Disable Accepting ICMP Redirects for All IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_accept_redirects mediumCCE-80917-8

Disable Accepting ICMP Redirects for All IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_accept_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_all_accept_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80917-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 2, 3, 7, 8, 9
cjis5.10.1.1
cobit5APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS05.02, DSS05.05, DSS05.07, DSS06.06
cui3.1.20
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a), SC-7(a)
nist-csfDE.CM-1, PR.DS-4, PR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.5
stigidRHEL-08-040279
stigrefSV-244553r1017353_rule
Description
To set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.accept_redirects = 0
Rationale
ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages modify the host's route table and are unauthenticated. An illicit ICMP redirect message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should be disabled unless absolutely required."

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.all.accept_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.accept_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.all.accept_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv4_conf_all_accept_redirects_value='0'


#
# Set runtime for net.ipv4.conf.all.accept_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects="$sysctl_net_ipv4_conf_all_accept_redirects_value"
fi

#
# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.accept_redirects = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.accept_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_accept_redirects_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.accept_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.accept_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80917-8"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80917-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040279
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_accept_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.all.accept_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80917-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040279
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_accept_redirects

- name: Comment out any occurrences of net.ipv4.conf.all.accept_redirects from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.all.accept_redirects
    replace: '#net.ipv4.conf.all.accept_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80917-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040279
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_accept_redirects
- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_all_accept_redirects_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.all.accept_redirects is set
  ansible.posix.sysctl:
    name: net.ipv4.conf.all.accept_redirects
    value: '{{ sysctl_net_ipv4_conf_all_accept_redirects_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80917-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040279
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_accept_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.conf.all.accept_redirects%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_redirects.conf
        overwrite: true
OVAL test results details

net.ipv4.conf.all.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1

net.ipv4.conf.all.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_accept_redirects:obj:1

net.ipv4.conf.all.accept_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_all_accept_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.all.accept_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.all.accept_redirects set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_redirects_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv4.conf.all.accept_redirects0
Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_accept_source_route mediumCCE-81011-9

Disable Kernel Parameter for Accepting Source-Routed Packets on all IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_accept_source_route
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_all_accept_source_route:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81011-9

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-7(a), CM-7(b), SC-5, CM-6(a), SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.8
stigidRHEL-08-040239
stigrefSV-244551r1017351_rule
Description
To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.accept_source_route = 0
Rationale
Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures. This requirement applies only to the forwarding of source-routerd traffic, such as when IPv4 forwarding is enabled and the system is functioning as a router.

Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required.
OVAL test results details

net.ipv4.conf.all.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_source_route_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1

net.ipv4.conf.all.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_source_route_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_accept_source_route:obj:1

net.ipv4.conf.all.accept_source_route static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_source_route_static_pkg_correct:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/usr/lib/sysctl.d/50-default.confnet.ipv4.conf.all.accept_source_route = 0

kernel runtime parameter net.ipv4.conf.all.accept_source_route set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_conf_all_accept_source_route_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv4.conf.all.accept_source_route0
Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_forwarding mediumCCE-86220-1

Disable Kernel Parameter for IPv4 Forwarding on all IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_forwarding
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_all_forwarding:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-86220-1

References:
nistCM-6(b)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040259
stigrefSV-250317r1017358_rule
Description
To set the runtime status of the net.ipv4.conf.all.forwarding kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.all.forwarding=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.forwarding = 0
Rationale
IP forwarding permits the kernel to forward packets from one network interface to another. The ability to forward packets between two networks is only appropriate for systems acting as routers.
Warnings
warning  There might be cases when certain applications can systematically override this option. One such case is Libvirt; a toolkit for managing of virtualization platforms. By default, Libvirt requires IP forwarding to be enabled to facilitate network communication between the virtualization host and guest machines. It enables IP forwarding after every reboot.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.all.forwarding from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.forwarding.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.all.forwarding" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv4_conf_all_forwarding_value='0'


#
# Set runtime for net.ipv4.conf.all.forwarding
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.all.forwarding="$sysctl_net_ipv4_conf_all_forwarding_value"
fi

#
# If net.ipv4.conf.all.forwarding present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.forwarding = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.forwarding")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_forwarding_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.forwarding\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.forwarding\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-86220-1"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86220-1
  - DISA-STIG-RHEL-08-040259
  - NIST-800-53-CM-6(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_forwarding

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.all.forwarding.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86220-1
  - DISA-STIG-RHEL-08-040259
  - NIST-800-53-CM-6(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_forwarding

- name: Comment out any occurrences of net.ipv4.conf.all.forwarding from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.all.forwarding
    replace: '#net.ipv4.conf.all.forwarding'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86220-1
  - DISA-STIG-RHEL-08-040259
  - NIST-800-53-CM-6(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_forwarding
- name: XCCDF Value sysctl_net_ipv4_conf_all_forwarding_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_all_forwarding_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.all.forwarding is set
  ansible.posix.sysctl:
    name: net.ipv4.conf.all.forwarding
    value: '{{ sysctl_net_ipv4_conf_all_forwarding_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86220-1
  - DISA-STIG-RHEL-08-040259
  - NIST-800-53-CM-6(b)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_forwarding
OVAL test results details

net.ipv4.conf.all.forwarding static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_forwarding_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_forwarding:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1

net.ipv4.conf.all.forwarding static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_forwarding_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_forwarding:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_forwarding:obj:1

net.ipv4.conf.all.forwarding static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_all_forwarding_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_all_forwarding:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.all.forwarding[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.all.forwarding set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_conf_all_forwarding_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv4.conf.all.forwarding1
Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_rp_filter mediumCCE-81021-8

Enable Kernel Parameter to Use Reverse Path Filtering on all IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_rp_filter
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_all_rp_filter:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-81021-8

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 2, 4, 6, 7, 8, 9
cobit5APO01.06, APO13.01, BAI04.04, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.07, DSS06.02
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.4.3.3
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-7(a), CM-7(b), CM-6(a), SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.PT-4
pcidssReq-1.4.3
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.7
pcidss41.4.3, 1.4
stigidRHEL-08-040285
stigrefSV-230549r1017311_rule
Description
To set the runtime status of the net.ipv4.conf.all.rp_filter kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.all.rp_filter=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.rp_filter = 1
Rationale
Enabling reverse path filtering drops packets with source addresses that should not have been able to be received on the interface they were received on. It should not be used on systems which are routers for complicated networks, but is helpful for end hosts and routers serving small networks.
OVAL test results details

net.ipv4.conf.all.rp_filter static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_rp_filter_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_rp_filter:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1

net.ipv4.conf.all.rp_filter static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_rp_filter_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_rp_filter:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_rp_filter:obj:1

net.ipv4.conf.all.rp_filter static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_all_rp_filter_static_pkg_correct:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/usr/lib/sysctl.d/50-default.confnet.ipv4.conf.all.rp_filter = 1

kernel runtime parameter net.ipv4.conf.all.rp_filter set to 1 or 2  oval:ssg-test_sysctl_net_ipv4_conf_all_rp_filter_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv4.conf.all.rp_filter1
Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_redirects mediumCCE-80919-4

Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_default_accept_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80919-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cjis5.10.1.1
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-7(a), CM-7(b), CM-6(a), SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
pcidssReq-1.4.3
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.5
pcidss41.4.3, 1.4
stigidRHEL-08-040209
stigrefSV-244550r1017350_rule
Description
To set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.accept_redirects = 0
Rationale
ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages modify the host's route table and are unauthenticated. An illicit ICMP redirect message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should be disabled unless absolutely required.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.default.accept_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.default.accept_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv4_conf_default_accept_redirects_value='0'


#
# Set runtime for net.ipv4.conf.default.accept_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects="$sysctl_net_ipv4_conf_default_accept_redirects_value"
fi

#
# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_redirects_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80919-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80919-4
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040209
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.3
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.default.accept_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80919-4
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040209
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.3
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_redirects

- name: Comment out any occurrences of net.ipv4.conf.default.accept_redirects from
    config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.default.accept_redirects
    replace: '#net.ipv4.conf.default.accept_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80919-4
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040209
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.3
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_redirects
- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_default_accept_redirects_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.default.accept_redirects is set
  ansible.posix.sysctl:
    name: net.ipv4.conf.default.accept_redirects
    value: '{{ sysctl_net_ipv4_conf_default_accept_redirects_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80919-4
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040209
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-7(a)
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.3
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.conf.default.accept_redirects%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_redirects.conf
        overwrite: true
OVAL test results details

net.ipv4.conf.default.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1

net.ipv4.conf.default.accept_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_accept_redirects:obj:1

net.ipv4.conf.default.accept_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_default_accept_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.default.accept_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.default.accept_redirects set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_redirects_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv4.conf.default.accept_redirects1
Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Defaultxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_source_route mediumCCE-80920-2

Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by Default

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_source_route
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_default_accept_source_route:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80920-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cjis5.10.1.1
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-7(a), CM-7(b), SC-5, SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.8
stigidRHEL-08-040249
stigrefSV-244552r1017352_rule
Description
To set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.accept_source_route = 0
Rationale
Source-routed packets allow the source of the packet to suggest routers forward the packet along a different path than configured on the router, which can be used to bypass network security measures.
Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required, such as when IPv4 forwarding is enabled and the system is legitimately functioning as a router.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.default.accept_source_route from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_source_route.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.default.accept_source_route" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv4_conf_default_accept_source_route_value='0'


#
# Set runtime for net.ipv4.conf.default.accept_source_route
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route="$sysctl_net_ipv4_conf_default_accept_source_route_value"
fi

#
# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_source_route = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_source_route")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_source_route_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_source_route\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80920-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80920-2
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040249
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_source_route

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.default.accept_source_route.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80920-2
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040249
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_source_route

- name: Comment out any occurrences of net.ipv4.conf.default.accept_source_route from
    config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.default.accept_source_route
    replace: '#net.ipv4.conf.default.accept_source_route'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80920-2
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040249
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_source_route
- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_source_route_value # promote to variable
  set_fact:
    sysctl_net_ipv4_conf_default_accept_source_route_value: !!str 0
  tags:
    - always

- name: Ensure sysctl net.ipv4.conf.default.accept_source_route is set
  ansible.posix.sysctl:
    name: net.ipv4.conf.default.accept_source_route
    value: '{{ sysctl_net_ipv4_conf_default_accept_source_route_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80920-2
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040249
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_accept_source_route

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.conf.default.accept_source_route%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_source_route.conf
        overwrite: true
OVAL test results details

net.ipv4.conf.default.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_source_route_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1

net.ipv4.conf.default.accept_source_route static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_source_route_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_accept_source_route:obj:1

net.ipv4.conf.default.accept_source_route static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_source_route_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_default_accept_source_route:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.default.accept_source_route[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.default.accept_source_route set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_conf_default_accept_source_route_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv4.conf.default.accept_source_route1
Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_echo_ignore_broadcasts mediumCCE-80922-8

Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_echo_ignore_broadcasts
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_icmp_echo_ignore_broadcasts:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80922-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cjis5.10.1.1
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-7(a), CM-7(b), SC-5
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
pcidssReq-1.4.3
os-srgSRG-OS-000480-GPOS-00227
cis3.3.4
pcidss41.4.2, 1.4
stigidRHEL-08-040230
stigrefSV-230537r1017299_rule
Description
To set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.icmp_echo_ignore_broadcasts = 1
Rationale
Responding to broadcast (ICMP) echoes facilitates network mapping and provides a vector for amplification attacks.
Ignoring ICMP echo requests (pings) sent to broadcast or multicast addresses makes the system slightly more difficult to enumerate on the network.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.icmp_echo_ignore_broadcasts" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"

sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value='1'


#
# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts="$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value"
fi

#
# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_echo_ignore_broadcasts")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_echo_ignore_broadcasts\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.icmp_echo_ignore_broadcasts\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80922-8"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80922-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040230
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_icmp_echo_ignore_broadcasts

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80922-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040230
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_icmp_echo_ignore_broadcasts

- name: Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.icmp_echo_ignore_broadcasts
    replace: '#net.ipv4.icmp_echo_ignore_broadcasts'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80922-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040230
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_icmp_echo_ignore_broadcasts
- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable
  set_fact:
    sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: !!str 1
  tags:
    - always

- name: Ensure sysctl net.ipv4.icmp_echo_ignore_broadcasts is set
  ansible.posix.sysctl:
    name: net.ipv4.icmp_echo_ignore_broadcasts
    value: '{{ sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value }}'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80922-8
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040230
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - PCI-DSS-Req-1.4.3
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_icmp_echo_ignore_broadcasts

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.icmp_echo_ignore_broadcasts%3D1%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_echo_ignore_broadcasts.conf
        overwrite: true
OVAL test results details

net.ipv4.icmp_echo_ignore_broadcasts static configuration  oval:ssg-test_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1

net.ipv4.icmp_echo_ignore_broadcasts static configuration  oval:ssg-test_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1

net.ipv4.icmp_echo_ignore_broadcasts static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_icmp_echo_ignore_broadcasts:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.icmp_echo_ignore_broadcasts[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.icmp_echo_ignore_broadcasts set to the appropriate value  oval:ssg-test_sysctl_net_ipv4_icmp_echo_ignore_broadcasts_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truenet.ipv4.icmp_echo_ignore_broadcasts1
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfacesxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_send_redirects mediumCCE-80918-6

Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_send_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_all_send_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80918-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cjis5.10.1.1
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-7(a), CM-7(b), SC-5, CM-6(a), SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.2
pcidss41.4.5, 1.4
stigidRHEL-08-040220
stigrefSV-230536r1017298_rule
Description
To set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.all.send_redirects = 0
Rationale
ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages contain information from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.send_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.all.send_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for net.ipv4.conf.all.send_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects="0"
fi

#
# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.send_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "0"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.send_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.send_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80918-6"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80918-6
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040220
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_send_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.all.send_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80918-6
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040220
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_send_redirects

- name: Comment out any occurrences of net.ipv4.conf.all.send_redirects from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.all.send_redirects
    replace: '#net.ipv4.conf.all.send_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80918-6
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040220
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_send_redirects

- name: Ensure sysctl net.ipv4.conf.all.send_redirects is set to 0
  ansible.posix.sysctl:
    name: net.ipv4.conf.all.send_redirects
    value: '0'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80918-6
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040220
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_all_send_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.conf.all.send_redirects%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_send_redirects.conf
        overwrite: true
OVAL test results details

net.ipv4.conf.all.send_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_send_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_send_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1

net.ipv4.conf.all.send_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_all_send_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_all_send_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_all_send_redirects:obj:1

net.ipv4.conf.all.send_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_all_send_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_all_send_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.all.send_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.all.send_redirects set to 0  oval:ssg-test_sysctl_net_ipv4_conf_all_send_redirects_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv4.conf.all.send_redirects1
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Defaultxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_send_redirects mediumCCE-80921-0

Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by Default

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_send_redirects
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_ipv4_conf_default_send_redirects:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80921-0

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9
cjis5.10.1.1
cobit5APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06
cui3.1.20
isa-62443-20094.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-007-3 R4, CIP-007-3 R4.1, CIP-007-3 R4.2, CIP-007-3 R5.1
nistCM-7(a), CM-7(b), SC-5, CM-6(a), SC-7(a)
nist-csfDE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR12
cis3.3.2
pcidss41.4.5, 1.4
stigidRHEL-08-040270
stigrefSV-230543r1017305_rule
Description
To set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command:
$ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.ipv4.conf.default.send_redirects = 0
Rationale
ICMP redirect messages are used by routers to inform hosts that a more direct route exists for a particular destination. These messages contain information from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.send_redirects.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.ipv4.conf.default.send_redirects" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for net.ipv4.conf.default.send_redirects
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects="0"
fi

#
# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.send_redirects")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "0"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.send_redirects\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.default.send_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80921-0"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80921-0
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040270
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_send_redirects

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.ipv4.conf.default.send_redirects.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80921-0
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040270
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_send_redirects

- name: Comment out any occurrences of net.ipv4.conf.default.send_redirects from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.ipv4.conf.default.send_redirects
    replace: '#net.ipv4.conf.default.send_redirects'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80921-0
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040270
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_send_redirects

- name: Ensure sysctl net.ipv4.conf.default.send_redirects is set to 0
  ansible.posix.sysctl:
    name: net.ipv4.conf.default.send_redirects
    value: '0'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80921-0
  - CJIS-5.10.1.1
  - DISA-STIG-RHEL-08-040270
  - NIST-800-171-3.1.20
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-SC-5
  - NIST-800-53-SC-7(a)
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.5
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_ipv4_conf_default_send_redirects

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.ipv4.conf.default.send_redirects%3D0%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_send_redirects.conf
        overwrite: true
OVAL test results details

net.ipv4.conf.default.send_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_send_redirects_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_send_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1

net.ipv4.conf.default.send_redirects static configuration  oval:ssg-test_sysctl_net_ipv4_conf_default_send_redirects_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_ipv4_conf_default_send_redirects:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_ipv4_conf_default_send_redirects:obj:1

net.ipv4.conf.default.send_redirects static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_ipv4_conf_default_send_redirects_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_ipv4_conf_default_send_redirects:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.ipv4.conf.default.send_redirects[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.ipv4.conf.default.send_redirects set to 0  oval:ssg-test_sysctl_net_ipv4_conf_default_send_redirects_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.ipv4.conf.default.send_redirects1
Disable ATM Supportxccdf_org.ssgproject.content_rule_kernel_module_atm_disabled mediumCCE-82028-2

Disable ATM Support

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_atm_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_atm_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-82028-2

References:
nistAC-18
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040021
stigrefSV-230494r1069310_rule
Description
The Asynchronous Transfer Mode (ATM) is a protocol operating on network, data link, and physical layers, based on virtual circuits and virtual paths. To configure the system to prevent the atm kernel module from being loaded, add the following line to the file /etc/modprobe.d/atm.conf:
install atm /bin/false
To configure the system to prevent the atm from being used, add the following line to file /etc/modprobe.d/atm.conf:
blacklist atm
Rationale
Disabling ATM protects the system against exploitation of any flaws in its implementation.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install atm" /etc/modprobe.d/atm.conf ; then
	
	sed -i 's#^install atm.*#install atm /bin/false#g' /etc/modprobe.d/atm.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/atm.conf
	echo "install atm /bin/false" >> /etc/modprobe.d/atm.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist atm$" /etc/modprobe.d/atm.conf ; then
	echo "blacklist atm" >> /etc/modprobe.d/atm.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82028-2
  - DISA-STIG-RHEL-08-040021
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_atm_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'atm' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/atm.conf
    regexp: install\s+atm
    line: install atm /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82028-2
  - DISA-STIG-RHEL-08-040021
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_atm_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'atm' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/atm.conf
    regexp: ^blacklist atm$
    line: blacklist atm
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82028-2
  - DISA-STIG-RHEL-08-040021
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_atm_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20atm%20/bin/false%0Ablacklist%20atm%0A
        mode: 0644
        path: /etc/modprobe.d/atm.conf
        overwrite: true
OVAL test results details

kernel module atm blacklisted  oval:ssg-test_kernmod_atm_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_atm_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+atm$1

kernel module atm disabled  oval:ssg-test_kernmod_atm_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_atm_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+atm\s+(/bin/false|/bin/true)$1
Disable CAN Supportxccdf_org.ssgproject.content_rule_kernel_module_can_disabled mediumCCE-82059-7

Disable CAN Support

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_can_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_can_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-82059-7

References:
nistAC-18
osppFMT_SMF_EXT.1
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
stigidRHEL-08-040022
stigrefSV-230495r1069311_rule
Description
The Controller Area Network (CAN) is a serial communications protocol which was initially developed for automotive and is now also used in marine, industrial, and medical applications. To configure the system to prevent the can kernel module from being loaded, add the following line to the file /etc/modprobe.d/can.conf:
install can /bin/false
To configure the system to prevent the can from being used, add the following line to file /etc/modprobe.d/can.conf:
blacklist can
Rationale
Disabling CAN protects the system against exploitation of any flaws in its implementation.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install can" /etc/modprobe.d/can.conf ; then
	
	sed -i 's#^install can.*#install can /bin/false#g' /etc/modprobe.d/can.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/can.conf
	echo "install can /bin/false" >> /etc/modprobe.d/can.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist can$" /etc/modprobe.d/can.conf ; then
	echo "blacklist can" >> /etc/modprobe.d/can.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82059-7
  - DISA-STIG-RHEL-08-040022
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_can_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'can' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/can.conf
    regexp: install\s+can
    line: install can /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82059-7
  - DISA-STIG-RHEL-08-040022
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_can_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'can' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/can.conf
    regexp: ^blacklist can$
    line: blacklist can
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82059-7
  - DISA-STIG-RHEL-08-040022
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_can_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20can%20/bin/false%0Ablacklist%20can%0A
        mode: 0644
        path: /etc/modprobe.d/can.conf
        overwrite: true
OVAL test results details

kernel module can blacklisted  oval:ssg-test_kernmod_can_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_can_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+can$1

kernel module can disabled  oval:ssg-test_kernmod_can_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_can_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+can\s+(/bin/false|/bin/true)$1
Disable IEEE 1394 (FireWire) Supportxccdf_org.ssgproject.content_rule_kernel_module_firewire-core_disabled lowCCE-82005-0

Disable IEEE 1394 (FireWire) Support

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_firewire-core_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_firewire-core_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitylow
Identifiers:

CCE-82005-0

References:
nistAC-18
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040026
stigrefSV-230499r1069315_rule
Description
The IEEE 1394 (FireWire) is a serial bus standard for high-speed real-time communication. To configure the system to prevent the firewire-core kernel module from being loaded, add the following line to the file /etc/modprobe.d/firewire-core.conf:
install firewire-core /bin/false
To configure the system to prevent the firewire-core from being used, add the following line to file /etc/modprobe.d/firewire-core.conf:
blacklist firewire-core
Rationale
Disabling FireWire protects the system against exploitation of any flaws in its implementation.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install firewire-core" /etc/modprobe.d/firewire-core.conf ; then
	
	sed -i 's#^install firewire-core.*#install firewire-core /bin/false#g' /etc/modprobe.d/firewire-core.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/firewire-core.conf
	echo "install firewire-core /bin/false" >> /etc/modprobe.d/firewire-core.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist firewire-core$" /etc/modprobe.d/firewire-core.conf ; then
	echo "blacklist firewire-core" >> /etc/modprobe.d/firewire-core.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82005-0
  - DISA-STIG-RHEL-08-040026
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_firewire-core_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'firewire-core' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/firewire-core.conf
    regexp: install\s+firewire-core
    line: install firewire-core /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82005-0
  - DISA-STIG-RHEL-08-040026
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_firewire-core_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'firewire-core' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/firewire-core.conf
    regexp: ^blacklist firewire-core$
    line: blacklist firewire-core
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82005-0
  - DISA-STIG-RHEL-08-040026
  - NIST-800-53-AC-18
  - disable_strategy
  - kernel_module_firewire-core_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20firewire-core%20/bin/false%0Ablacklist%20firewire-core%0A
        mode: 0644
        path: /etc/modprobe.d/firewire-core.conf
        overwrite: true
OVAL test results details

kernel module firewire-core blacklisted  oval:ssg-test_kernmod_firewire-core_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_firewire-core_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+firewire-core$1

kernel module firewire-core disabled  oval:ssg-test_kernmod_firewire-core_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_firewire-core_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+firewire-core\s+(/bin/false|/bin/true)$1
Disable SCTP Supportxccdf_org.ssgproject.content_rule_kernel_module_sctp_disabled mediumCCE-80834-5

Disable SCTP Support

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_sctp_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_sctp_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80834-5

References:
cis-csc11, 14, 3, 9
cjis5.10.1
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.4.6
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
osppFMT_SMF_EXT.1
pcidssReq-1.4.2
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
cis3.2.4
pcidss41.4.2, 1.4
stigidRHEL-08-040023
stigrefSV-230496r1069312_rule
Description
The Stream Control Transmission Protocol (SCTP) is a transport layer protocol, designed to support the idea of message-oriented communication, with several streams of messages within one connection. To configure the system to prevent the sctp kernel module from being loaded, add the following line to the file /etc/modprobe.d/sctp.conf:
install sctp /bin/false
To configure the system to prevent the sctp from being used, add the following line to file /etc/modprobe.d/sctp.conf:
blacklist sctp
Rationale
Disabling SCTP protects the system against exploitation of any flaws in its implementation.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install sctp" /etc/modprobe.d/sctp.conf ; then
	
	sed -i 's#^install sctp.*#install sctp /bin/false#g' /etc/modprobe.d/sctp.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf
	echo "install sctp /bin/false" >> /etc/modprobe.d/sctp.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist sctp$" /etc/modprobe.d/sctp.conf ; then
	echo "blacklist sctp" >> /etc/modprobe.d/sctp.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80834-5
  - CJIS-5.10.1
  - DISA-STIG-RHEL-08-040023
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.2
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - kernel_module_sctp_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'sctp' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/sctp.conf
    regexp: install\s+sctp
    line: install sctp /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80834-5
  - CJIS-5.10.1
  - DISA-STIG-RHEL-08-040023
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.2
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - kernel_module_sctp_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'sctp' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/sctp.conf
    regexp: ^blacklist sctp$
    line: blacklist sctp
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80834-5
  - CJIS-5.10.1
  - DISA-STIG-RHEL-08-040023
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-1.4.2
  - PCI-DSSv4-1.4
  - PCI-DSSv4-1.4.2
  - disable_strategy
  - kernel_module_sctp_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20sctp%20/bin/false%0Ablacklist%20sctp%0A
        mode: 0644
        path: /etc/modprobe.d/sctp.conf
        overwrite: true
OVAL test results details

kernel module sctp blacklisted  oval:ssg-test_kernmod_sctp_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_sctp_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+sctp$1

kernel module sctp disabled  oval:ssg-test_kernmod_sctp_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_sctp_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+sctp\s+(/bin/false|/bin/true)$1
Disable TIPC Supportxccdf_org.ssgproject.content_rule_kernel_module_tipc_disabled lowCCE-82297-3

Disable TIPC Support

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_tipc_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_tipc_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitylow
Identifiers:

CCE-82297-3

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000095-GPOS-00049
cis3.2.2
stigidRHEL-08-040024
stigrefSV-230497r1069313_rule
Description
The Transparent Inter-Process Communication (TIPC) protocol is designed to provide communications between nodes in a cluster. To configure the system to prevent the tipc kernel module from being loaded, add the following line to the file /etc/modprobe.d/tipc.conf:
install tipc /bin/false
To configure the system to prevent the tipc from being used, add the following line to file /etc/modprobe.d/tipc.conf:
blacklist tipc
Rationale
Disabling TIPC protects the system against exploitation of any flaws in its implementation.
Warnings
warning  This configuration baseline was created to deploy the base operating system for general purpose workloads. When the operating system is configured for certain purposes, such as a node in High Performance Computing cluster, it is expected that the tipc kernel module will be loaded.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install tipc" /etc/modprobe.d/tipc.conf ; then
	
	sed -i 's#^install tipc.*#install tipc /bin/false#g' /etc/modprobe.d/tipc.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/tipc.conf
	echo "install tipc /bin/false" >> /etc/modprobe.d/tipc.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist tipc$" /etc/modprobe.d/tipc.conf ; then
	echo "blacklist tipc" >> /etc/modprobe.d/tipc.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82297-3
  - DISA-STIG-RHEL-08-040024
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_tipc_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'tipc' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/tipc.conf
    regexp: install\s+tipc
    line: install tipc /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82297-3
  - DISA-STIG-RHEL-08-040024
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_tipc_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'tipc' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/tipc.conf
    regexp: ^blacklist tipc$
    line: blacklist tipc
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82297-3
  - DISA-STIG-RHEL-08-040024
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_tipc_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20tipc%20/bin/false%0Ablacklist%20tipc%0A
        mode: 0644
        path: /etc/modprobe.d/tipc.conf
        overwrite: true
OVAL test results details

kernel module tipc blacklisted  oval:ssg-test_kernmod_tipc_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_tipc_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+tipc$1

kernel module tipc disabled  oval:ssg-test_kernmod_tipc_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_tipc_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+tipc\s+(/bin/false|/bin/true)$1
Disable Bluetooth Kernel Modulexccdf_org.ssgproject.content_rule_kernel_module_bluetooth_disabled mediumCCE-80832-9

Disable Bluetooth Kernel Module

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_bluetooth_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_bluetooth_disabled:def:1
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-80832-9

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cjis5.13.1.3
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
cui3.1.16
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistAC-18(a), AC-18(3), CM-7(a), CM-7(b), CM-6(a), MP-7
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
osppFMT_SMF_EXT.1
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000300-GPOS-00118
stigidRHEL-08-040111
stigrefSV-230507r1017287_rule
Description
The kernel's module loading system can be configured to prevent loading of the Bluetooth module. Add the following to the appropriate /etc/modprobe.d configuration file to prevent the loading of the Bluetooth module:
install bluetooth /bin/true
Rationale
If Bluetooth functionality must be disabled, preventing the kernel from loading the kernel module provides an additional safeguard against its activation.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install bluetooth" /etc/modprobe.d/bluetooth.conf ; then
	
	sed -i 's#^install bluetooth.*#install bluetooth /bin/false#g' /etc/modprobe.d/bluetooth.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/bluetooth.conf
	echo "install bluetooth /bin/false" >> /etc/modprobe.d/bluetooth.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist bluetooth$" /etc/modprobe.d/bluetooth.conf ; then
	echo "blacklist bluetooth" >> /etc/modprobe.d/bluetooth.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80832-9
  - CJIS-5.13.1.3
  - DISA-STIG-RHEL-08-040111
  - NIST-800-171-3.1.16
  - NIST-800-53-AC-18(3)
  - NIST-800-53-AC-18(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - disable_strategy
  - kernel_module_bluetooth_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'bluetooth' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/bluetooth.conf
    regexp: install\s+bluetooth
    line: install bluetooth /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80832-9
  - CJIS-5.13.1.3
  - DISA-STIG-RHEL-08-040111
  - NIST-800-171-3.1.16
  - NIST-800-53-AC-18(3)
  - NIST-800-53-AC-18(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - disable_strategy
  - kernel_module_bluetooth_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'bluetooth' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/bluetooth.conf
    regexp: ^blacklist bluetooth$
    line: blacklist bluetooth
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80832-9
  - CJIS-5.13.1.3
  - DISA-STIG-RHEL-08-040111
  - NIST-800-171-3.1.16
  - NIST-800-53-AC-18(3)
  - NIST-800-53-AC-18(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - disable_strategy
  - kernel_module_bluetooth_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20bluetooth%20/bin/false%0Ablacklist%20bluetooth%0A
        mode: 0644
        path: /etc/modprobe.d/bluetooth.conf
        overwrite: true
OVAL test results details

kernel module bluetooth blacklisted  oval:ssg-test_kernmod_bluetooth_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_bluetooth_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+bluetooth$1

kernel module bluetooth disabled  oval:ssg-test_kernmod_bluetooth_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_bluetooth_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+bluetooth\s+(/bin/false|/bin/true)$1
Deactivate Wireless Network Interfacesxccdf_org.ssgproject.content_rule_wireless_disable_interfaces mediumCCE-83501-7

Deactivate Wireless Network Interfaces

Rule IDxccdf_org.ssgproject.content_rule_wireless_disable_interfaces
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:05:37-05:00
Severitymedium
Identifiers:

CCE-83501-7

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
cui3.1.16
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
ism1315, 1319
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistAC-18(a), AC-18(3), CM-7(a), CM-7(b), CM-6(a), MP-7
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
pcidssReq-1.3.3
os-srgSRG-OS-000299-GPOS-00117, SRG-OS-000300-GPOS-00118, SRG-OS-000424-GPOS-00188, SRG-OS-000481-GPOS-00481
cis3.1.2
pcidss41.3.3, 1.3
stigidRHEL-08-040110
stigrefSV-230506r1017286_rule
Description
Deactivating wireless network interfaces should prevent normal usage of the wireless capability.

Configure the system to disable all wireless network interfaces with the following command:
$ sudo nmcli radio all off
Rationale
The use of wireless networking can introduce many different attack vectors into the organization's network. Common attack vectors such as malicious association and ad hoc networks will allow an attacker to spoof a wireless access point (AP), allowing validated systems to connect to the malicious AP and enabling the attacker to monitor and record network traffic. These malicious APs can also serve to create a man-in-the-middle attack or be used to create a denial of service to valid network resources.
Configure Multiple DNS Servers in /etc/resolv.confxccdf_org.ssgproject.content_rule_network_configure_name_resolution mediumCCE-84049-6

Configure Multiple DNS Servers in /etc/resolv.conf

Rule IDxccdf_org.ssgproject.content_rule_network_configure_name_resolution
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-network_configure_name_resolution:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-84049-6

References:
cis-csc12, 15, 8
cobit5APO13.01, DSS05.02
isa-62443-2013SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.13.1.1, A.13.2.1, A.14.1.3
nistSC-20(a), CM-6(a)
nist-csfPR.PT-4
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010680
stigrefSV-230316r1044801_rule
Description
Determine whether the system is using local or DNS name resolution with the following command:
$ sudo grep hosts /etc/nsswitch.conf
hosts: files dns
If the DNS entry is missing from the host's line in the "/etc/nsswitch.conf" file, the "/etc/resolv.conf" file must be empty. Verify the "/etc/resolv.conf" file is empty with the following command:
$ sudo ls -al /etc/resolv.conf
-rw-r--r-- 1 root root 0 Aug 19 08:31 resolv.conf
If the DNS entry is found on the host's line of the "/etc/nsswitch.conf" file, then verify the following:
Multiple Domain Name System (DNS) Servers should be configured in /etc/resolv.conf. This provides redundant name resolution services in the event that a domain server crashes. To configure the system to contain as least 2 DNS servers, add a corresponding nameserver ip_address entry in /etc/resolv.conf for each DNS server where ip_address is the IP address of a valid DNS server. For example:
search example.com
nameserver 192.168.0.1
nameserver 192.168.0.2
Rationale
To provide availability for name resolution services, multiple redundant name servers are mandated. A failure in name resolution could lead to the failure of security functions requiring name resolution, which may include time synchronization, centralized authentication, and remote system logging.
Warnings
warning  This rule doesn't come with a remediation, the IP addresses of local authoritative name servers need to be added by the administrator.
OVAL test results details

check if dns is set in host line in /etc/nsswitch.conf  oval:ssg-test_host_line_dns_parameter_nsswitch:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/nsswitch.confhosts: files dns myhostname

check if more than one nameserver in /etc/resolv.conf  oval:ssg-test_network_configure_name_resolution:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/resolv.confnameserver 8.8.8.8

check if dns is set in host line in /etc/nsswitch.conf  oval:ssg-test_host_line_dns_parameter_nsswitch:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/nsswitch.confhosts: files dns myhostname

check if /etc/resolv.conf is empty  oval:ssg-test_file_empty_resolv:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
false/etc/resolv.confregular0068rw-r--r-- 
Ensure System is Not Acting as a Network Snifferxccdf_org.ssgproject.content_rule_network_sniffer_disabled mediumCCE-82283-3

Ensure System is Not Acting as a Network Sniffer

Rule IDxccdf_org.ssgproject.content_rule_network_sniffer_disabled
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-network_sniffer_disabled:def:1
Time2025-11-18T17:05:31-05:00
Severitymedium
Identifiers:

CCE-82283-3

References:
cis-csc1, 11, 14, 3, 9
cobit5APO11.06, APO12.06, BAI03.10, BAI09.01, BAI09.02, BAI09.03, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.05, DSS04.05, DSS05.02, DSS05.05, DSS06.06
isa-62443-20094.2.3.4, 4.3.3.3.7, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6, SR 7.8
iso27001-2013A.11.1.2, A.11.2.4, A.11.2.5, A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.16.1.6, A.8.1.1, A.8.1.2, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a), CM-7(2), MA-3
nist-csfDE.DP-5, ID.AM-1, PR.IP-1, PR.MA-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
pcidss41.4.5, 1.4
stigidRHEL-08-040330
stigrefSV-230554r1017316_rule
Description
The system should not be acting as a network sniffer, which can capture all traffic on the network to which it is connected. Run the following to determine if any interface is running in promiscuous mode:
$ ip link | grep PROMISC
Promiscuous mode of an interface can be disabled with the following command:
$ sudo ip link set dev device_name multicast off promisc off
Rationale
Network interfaces in promiscuous mode allow for the capture of all network traffic visible to the system. If unauthorized individuals can access these applications, it may allow them to collect information such as logon IDs, passwords, and key exchanges between systems.

If the system is being used to perform a network troubleshooting function, the use of these tools must be documented with the Information Systems Security Manager (ISSM) and restricted to only authorized personnel.
OVAL test results details

check all network interfaces for PROMISC flag  oval:ssg-test_promisc_interfaces:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_promisc_interfaces:obj:1 of type interface_object
NameFilter
^.*$oval:ssg-state_promisc:ste:1
Verify Group Who Owns /var/log Directoryxccdf_org.ssgproject.content_rule_file_groupowner_var_log mediumCCE-83659-3

Verify Group Who Owns /var/log Directory

Rule IDxccdf_org.ssgproject.content_rule_file_groupowner_var_log
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_groupowner_var_log:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83659-3

References:
os-srgSRG-OS-000206-GPOS-00084
app-srg-ctrSRG-APP-000118-CTR-000240
stigidRHEL-08-010260
stigrefSV-230250r1017068_rule
Description
To properly set the group owner of /var/log, run the command:
$ sudo chgrp root /var/log
Rationale
The /var/log directory contains files with logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing group ownership of /var/log/  oval:ssg-test_file_groupowner_var_log_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupowner_var_log_0:obj:1 of type file_object
PathFilenameFilterFilter
/var/logno valueoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupowner_var_log_0_0:ste:1
Verify Group Who Owns /var/log/messages Filexccdf_org.ssgproject.content_rule_file_groupowner_var_log_messages mediumCCE-83660-1

Verify Group Who Owns /var/log/messages File

Rule IDxccdf_org.ssgproject.content_rule_file_groupowner_var_log_messages
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_groupowner_var_log_messages:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83660-1

References:
os-srgSRG-OS-000206-GPOS-00084
stigidRHEL-08-010230
stigrefSV-230247r1017065_rule
Description
To properly set the group owner of /var/log/messages, run the command:
$ sudo chgrp root /var/log/messages
Rationale
The /var/log/messages file contains logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing group ownership of /var/log/messages  oval:ssg-test_file_groupowner_var_log_messages_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupowner_var_log_messages_0:obj:1 of type file_object
FilepathFilterFilter
/var/log/messagesoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupowner_var_log_messages_0_0:ste:1
Verify User Who Owns /var/log Directoryxccdf_org.ssgproject.content_rule_file_owner_var_log mediumCCE-83661-9

Verify User Who Owns /var/log Directory

Rule IDxccdf_org.ssgproject.content_rule_file_owner_var_log
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_owner_var_log:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83661-9

References:
os-srgSRG-OS-000206-GPOS-00084
app-srg-ctrSRG-APP-000118-CTR-000240
stigidRHEL-08-010250
stigrefSV-230249r1017067_rule
Description
To properly set the owner of /var/log, run the command:
$ sudo chown root /var/log 
Rationale
The /var/log directory contains files with logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing user ownership of /var/log/  oval:ssg-test_file_owner_var_log_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_owner_var_log_0:obj:1 of type file_object
PathFilenameFilterFilter
/var/logno valueoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_owner_var_log_0_0:ste:1
Verify User Who Owns /var/log/messages Filexccdf_org.ssgproject.content_rule_file_owner_var_log_messages mediumCCE-83662-7

Verify User Who Owns /var/log/messages File

Rule IDxccdf_org.ssgproject.content_rule_file_owner_var_log_messages
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_owner_var_log_messages:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83662-7

References:
os-srgSRG-OS-000206-GPOS-00084
stigidRHEL-08-010220
stigrefSV-230246r1017064_rule
Description
To properly set the owner of /var/log/messages, run the command:
$ sudo chown root /var/log/messages 
Rationale
The /var/log/messages file contains logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing user ownership of /var/log/messages  oval:ssg-test_file_owner_var_log_messages_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_owner_var_log_messages_0:obj:1 of type file_object
FilepathFilterFilter
/var/log/messagesoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_owner_var_log_messages_0_0:ste:1
Verify Permissions on /var/log Directoryxccdf_org.ssgproject.content_rule_file_permissions_var_log mediumCCE-83663-5

Verify Permissions on /var/log Directory

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_var_log
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_var_log:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83663-5

References:
os-srgSRG-OS-000206-GPOS-00084
app-srg-ctrSRG-APP-000118-CTR-000240
stigidRHEL-08-010240
stigrefSV-230248r1069291_rule
Description
To properly set the permissions of /var/log, run the command:
$ sudo chmod 0755 /var/log
Rationale
The /var/log directory contains files with logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing mode of /var/log/  oval:ssg-test_file_permissions_var_log_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_var_log_0:obj:1 of type file_object
PathFilenameFilterFilter
/var/logno valueoval:ssg-exclude_symlinks__var_log:ste:1oval:ssg-state_file_permissions_var_log_0_mode_0755or_stricter_:ste:1
Verify Permissions on /var/log/messages Filexccdf_org.ssgproject.content_rule_file_permissions_var_log_messages mediumCCE-83665-0

Verify Permissions on /var/log/messages File

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_var_log_messages
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_var_log_messages:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83665-0

References:
os-srgSRG-OS-000206-GPOS-00084
stigidRHEL-08-010210
stigrefSV-230245r1017063_rule
Description
To properly set the permissions of /var/log/messages, run the command:
$ sudo chmod 0600 /var/log/messages
Rationale
The /var/log/messages file contains logs of error messages in the system and should only be accessed by authorized personnel.
OVAL test results details

Testing mode of /var/log/messages  oval:ssg-test_file_permissions_var_log_messages_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_var_log_messages_0:obj:1 of type file_object
FilepathFilterFilter
/var/log/messagesoval:ssg-exclude_symlinks__var_log_messages:ste:1oval:ssg-state_file_permissions_var_log_messages_0_mode_0600or_stricter_:ste:1
Verify that Shared Library Directories Have Root Group Ownershipxccdf_org.ssgproject.content_rule_dir_group_ownership_library_dirs mediumCCE-85894-4

Verify that Shared Library Directories Have Root Group Ownership

Rule IDxccdf_org.ssgproject.content_rule_dir_group_ownership_library_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_group_ownership_library_dirs:def:1
Time2025-11-18T17:06:35-05:00
Severitymedium
Identifiers:

CCE-85894-4

References:
nistCM-5(6), CM-5(6).1
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010351
stigrefSV-251709r1017364_rule
Description
System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also stored in /lib/modules. All files in these directories should be group-owned by the root user. If the directories, is found to be owned by a user other than root correct its ownership with the following command:
$ sudo chgrp root DIR
Rationale
Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Proper ownership of library directories is necessary to protect the integrity of the system.
OVAL test results details

Testing group ownership of /lib/  oval:ssg-test_file_groupownerdir_group_ownership_library_dirs_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerdir_group_ownership_library_dirs_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/libno valueoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_0_0:ste:1

Testing group ownership of /lib64/  oval:ssg-test_file_groupownerdir_group_ownership_library_dirs_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerdir_group_ownership_library_dirs_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64no valueoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_0_0:ste:1

Testing group ownership of /usr/lib/  oval:ssg-test_file_groupownerdir_group_ownership_library_dirs_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerdir_group_ownership_library_dirs_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/libno valueoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_0_0:ste:1

Testing group ownership of /usr/lib64/  oval:ssg-test_file_groupownerdir_group_ownership_library_dirs_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerdir_group_ownership_library_dirs_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64no valueoval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerdir_group_ownership_library_dirs_0_0:ste:1
Verify that Shared Library Directories Have Root Ownershipxccdf_org.ssgproject.content_rule_dir_ownership_library_dirs mediumCCE-89021-0

Verify that Shared Library Directories Have Root Ownership

Rule IDxccdf_org.ssgproject.content_rule_dir_ownership_library_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_ownership_library_dirs:def:1
Time2025-11-18T17:06:36-05:00
Severitymedium
Identifiers:

CCE-89021-0

References:
nistCM-5(6), CM-5(6).1
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010341
stigrefSV-251708r1017362_rule
Description
System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also stored in /lib/modules. All files in these directories should be owned by the root user. If the directories, is found to be owned by a user other than root correct its ownership with the following command:
$ sudo chown root DIR
Rationale
Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Proper ownership of library directories is necessary to protect the integrity of the system.
OVAL test results details

Testing user ownership of /lib/  oval:ssg-test_file_ownerdir_ownership_library_dirs_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerdir_ownership_library_dirs_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/libno valueoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerdir_ownership_library_dirs_0_0:ste:1

Testing user ownership of /lib64/  oval:ssg-test_file_ownerdir_ownership_library_dirs_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerdir_ownership_library_dirs_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64no valueoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerdir_ownership_library_dirs_0_0:ste:1

Testing user ownership of /usr/lib/  oval:ssg-test_file_ownerdir_ownership_library_dirs_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerdir_ownership_library_dirs_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/libno valueoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerdir_ownership_library_dirs_0_0:ste:1

Testing user ownership of /usr/lib64/  oval:ssg-test_file_ownerdir_ownership_library_dirs_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownerdir_ownership_library_dirs_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64no valueoval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownerdir_ownership_library_dirs_0_0:ste:1
Verify that Shared Library Directories Have Restrictive Permissionsxccdf_org.ssgproject.content_rule_dir_permissions_library_dirs mediumCCE-88692-9

Verify that Shared Library Directories Have Restrictive Permissions

Rule IDxccdf_org.ssgproject.content_rule_dir_permissions_library_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_permissions_library_dirs:def:1
Time2025-11-18T17:06:38-05:00
Severitymedium
Identifiers:

CCE-88692-9

References:
nerc-cipCIP-003-8 R6
nistCM-5, CM-5(6), CM-5(6).1
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010331
stigrefSV-251707r1017360_rule
Description
System-wide shared library directories, which contain are linked to executables during process load time or run time, are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are stored in /lib/modules. All sub-directories in these directories should not be group-writable or world-writable. If any file in these directories is found to be group-writable or world-writable, correct its permission with the following command:
$ sudo chmod go-w DIR
Rationale
If the operating system were to allow any user to make changes to software libraries, then those changes might be implemented without undergoing the appropriate testing and approvals that are part of a robust change management process. This requirement applies to operating systems with software libraries that are accessible and configurable, as in the case of interpreted languages. Software libraries also include privileged programs which execute with escalated privileges. Only qualified and authorized individuals must be allowed to obtain access to information system components for purposes of initiating changes, including upgrades and modifications.
OVAL test results details

Testing mode of /lib/  oval:ssg-test_file_permissionsdir_permissions_library_dirs_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsdir_permissions_library_dirs_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/libno valueoval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1oval:ssg-state_file_permissionsdir_permissions_library_dirs_0_mode_7755or_stricter_:ste:1

Testing mode of /lib64/  oval:ssg-test_file_permissionsdir_permissions_library_dirs_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsdir_permissions_library_dirs_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64no valueoval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1oval:ssg-state_file_permissionsdir_permissions_library_dirs_1_mode_7755or_stricter_:ste:1

Testing mode of /usr/lib/  oval:ssg-test_file_permissionsdir_permissions_library_dirs_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsdir_permissions_library_dirs_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/libno valueoval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1oval:ssg-state_file_permissionsdir_permissions_library_dirs_2_mode_7755or_stricter_:ste:1

Testing mode of /usr/lib64/  oval:ssg-test_file_permissionsdir_permissions_library_dirs_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissionsdir_permissions_library_dirs_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64no valueoval:ssg-exclude_symlinks_dir_permissions_library_dirs:ste:1oval:ssg-state_file_permissionsdir_permissions_library_dirs_3_mode_7755or_stricter_:ste:1
Verify that system commands files are group owned by root or a system accountxccdf_org.ssgproject.content_rule_file_groupownership_system_commands_dirs mediumCCE-86519-6

Verify that system commands files are group owned by root or a system account

Rule IDxccdf_org.ssgproject.content_rule_file_groupownership_system_commands_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_groupownership_system_commands_dirs:def:1
Time2025-11-18T17:06:38-05:00
Severitymedium
Identifiers:

CCE-86519-6

References:
nistCM-5(6), CM-5(6).1
os-srgSRG-OS-000259-GPOS-00100
anssiR50
stigidRHEL-08-010320
stigrefSV-230259r1017079_rule
Description
System commands files are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/sbin
/usr/local/bin
/usr/local/sbin
All files in these directories should be owned by the root group, or a system account. If the directory, or any file in these directories, is found to be owned by a group other than root or a a system account correct its ownership with the following command:
$ sudo chgrp root FILE
Rationale
If the operating system allows any user to make changes to software libraries, then those changes might be implemented without undergoing the appropriate testing and approvals that are part of a robust change management process. This requirement applies to operating systems with software libraries that are accessible and configurable, as in the case of interpreted languages. Software libraries also include privileged programs which execute with escalated privileges. Only qualified and authorized individuals must be allowed to obtain access to information system components for purposes of initiating changes, including upgrades and modifications.
OVAL test results details

system commands are owned by root or a system account  oval:ssg-test_groupownership_system_commands_dirs:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_groupownership_system_commands_dirs:obj:1 of type file_object
PathFilenameFilterFilter
^\/s?bin|^\/usr\/s?bin|^\/usr\/local\/s?bin^.*$oval:ssg-state_groupowner_system_commands_dirs_not_root_or_system_account:ste:1oval:ssg-state_groupowner_system_commands_dirs_symlink:ste:1
Verify that System Executables Have Root Ownershipxccdf_org.ssgproject.content_rule_file_ownership_binary_dirs mediumCCE-80806-3

Verify that System Executables Have Root Ownership

Rule IDxccdf_org.ssgproject.content_rule_file_ownership_binary_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_ownership_binary_dirs:def:1
Time2025-11-18T17:06:39-05:00
Severitymedium
Identifiers:

CCE-80806-3

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-5(6), CM-5(6).1, CM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000259-GPOS-00100
anssiR50
stigidRHEL-08-010310
stigrefSV-230258r1017078_rule
Description
System executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should be owned by the root user. If any file FILE in these directories is found to be owned by a user other than root, correct its ownership with the following command:
$ sudo chown root FILE
Rationale
System binaries are executed by privileged users as well as system services, and restrictive permissions are necessary to ensure that their execution of these programs cannot be co-opted.
OVAL test results details

binary directories uid root  oval:ssg-test_ownership_binary_directories:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_binary_directories:obj:1 of type file_object
PathFilenameFilter
^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexecno valueoval:ssg-state_owner_binaries_not_root:ste:1

binary files uid root  oval:ssg-test_ownership_binary_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_binary_files:obj:1 of type file_object
PathFilenameFilter
^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec^.*$oval:ssg-state_owner_binaries_not_root:ste:1
Verify that Shared Library Files Have Root Ownershipxccdf_org.ssgproject.content_rule_file_ownership_library_dirs mediumCCE-80807-1

Verify that Shared Library Files Have Root Ownership

Rule IDxccdf_org.ssgproject.content_rule_file_ownership_library_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_ownership_library_dirs:def:1
Time2025-11-18T17:06:46-05:00
Severitymedium
Identifiers:

CCE-80807-1

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-5(6), CM-5(6).1, CM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010340
stigrefSV-230261r1101891_rule
Description
System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also stored in /lib/modules. All files in these directories should be owned by the root user. If the directory, or any file in these directories, is found to be owned by a user other than root correct its ownership with the following command:
$ sudo chown root FILE
Rationale
Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Proper ownership is necessary to protect the integrity of the system.
OVAL test results details

Testing user ownership of /lib/  oval:ssg-test_file_ownership_library_dirs_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_library_dirs_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib^.*$oval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownership_library_dirs_0_0:ste:1

Testing user ownership of /lib64/  oval:ssg-test_file_ownership_library_dirs_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_library_dirs_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64^.*$oval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownership_library_dirs_0_0:ste:1

Testing user ownership of /usr/lib/  oval:ssg-test_file_ownership_library_dirs_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_library_dirs_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib^.*$oval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownership_library_dirs_0_0:ste:1

Testing user ownership of /usr/lib64/  oval:ssg-test_file_ownership_library_dirs_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_ownership_library_dirs_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64^.*$oval:ssg-symlink_file_owner:ste:1oval:ssg-state_file_ownership_library_dirs_0_0:ste:1
Verify that System Executables Have Restrictive Permissionsxccdf_org.ssgproject.content_rule_file_permissions_binary_dirs mediumCCE-80809-7

Verify that System Executables Have Restrictive Permissions

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_binary_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_binary_dirs:def:1
Time2025-11-18T17:06:46-05:00
Severitymedium
Identifiers:

CCE-80809-7

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-5(6), CM-5(6).1, CM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000259-GPOS-00100
anssiR50
stigidRHEL-08-010300
stigrefSV-230257r1017077_rule
Description
System executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should not be group-writable or world-writable. If any file FILE in these directories is found to be group-writable or world-writable, correct its permission with the following command:
$ sudo chmod go-w FILE
Rationale
System binaries are executed by privileged users, as well as system services, and restrictive permissions are necessary to ensure execution of these programs cannot be co-opted.
OVAL test results details

binary files go-w  oval:ssg-test_perms_binary_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_binary_files:obj:1 of type file_object
PathFilenameFilterFilter
^\/(|s)bin|^\/usr\/(|local\/)(|s)bin|^\/usr\/libexec^.*$oval:ssg-state_perms_binary_files_nogroupwrite_noworldwrite:ste:1oval:ssg-state_perms_binary_files_symlink:ste:1
Verify that Shared Library Files Have Restrictive Permissionsxccdf_org.ssgproject.content_rule_file_permissions_library_dirs mediumCCE-80815-4

Verify that Shared Library Files Have Restrictive Permissions

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_library_dirs
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_library_dirs:def:1
Time2025-11-18T17:06:54-05:00
Severitymedium
Identifiers:

CCE-80815-4

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), CM-5(6), CM-5(6).1, AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010330
stigrefSV-230260r1101888_rule
Description
System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are stored in /lib/modules. All files in these directories should not be group-writable or world-writable. If any file in these directories is found to be group-writable or world-writable, correct its permission with the following command:
$ sudo chmod go-w FILE
Rationale
Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Restrictive permissions are necessary to protect the integrity of the system.
OVAL test results details

Testing mode of /lib/  oval:ssg-test_file_permissions_library_dirs_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_library_dirs_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib^.*$oval:ssg-exclude_symlinks__library_dirs:ste:1oval:ssg-state_file_permissions_library_dirs_0_mode_7755or_stricter_:ste:1

Testing mode of /lib64/  oval:ssg-test_file_permissions_library_dirs_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_library_dirs_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64^.*$oval:ssg-exclude_symlinks__library_dirs:ste:1oval:ssg-state_file_permissions_library_dirs_1_mode_7755or_stricter_:ste:1

Testing mode of /usr/lib/  oval:ssg-test_file_permissions_library_dirs_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_library_dirs_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib^.*$oval:ssg-exclude_symlinks__library_dirs:ste:1oval:ssg-state_file_permissions_library_dirs_2_mode_7755or_stricter_:ste:1

Testing mode of /usr/lib64/  oval:ssg-test_file_permissions_library_dirs_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_library_dirs_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64^.*$oval:ssg-exclude_symlinks__library_dirs:ste:1oval:ssg-state_file_permissions_library_dirs_3_mode_7755or_stricter_:ste:1
Verify the system-wide library files in directories "/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root.xccdf_org.ssgproject.content_rule_root_permissions_syslibrary_files mediumCCE-86523-8

Verify the system-wide library files in directories "/lib", "/lib64", "/usr/lib/" and "/usr/lib64" are group-owned by root.

Rule IDxccdf_org.ssgproject.content_rule_root_permissions_syslibrary_files
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-root_permissions_syslibrary_files:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-86523-8

References:
nistCM-5(6), CM-5(6).1
os-srgSRG-OS-000259-GPOS-00100
stigidRHEL-08-010350
stigrefSV-230262r1101894_rule
Description
System-wide library files are stored in the following directories by default:
/lib
/lib64
/usr/lib
/usr/lib64
All system-wide shared library files should be protected from unauthorised access. If any of these files is not group-owned by root, correct its group-owner with the following command:
$ sudo chgrp root FILE
Rationale
If the operating system were to allow any user to make changes to software libraries, then those changes might be implemented without undergoing the appropriate testing and approvals that are part of a robust change management process. This requirement applies to operating systems with software libraries that are accessible and configurable, as in the case of interpreted languages. Software libraries also include privileged programs which execute with escalated privileges. Only qualified and authorized individuals must be allowed to obtain access to information system components for purposes of initiating changes, including upgrades and modifications.
OVAL test results details

Testing group ownership of /lib/  oval:ssg-test_file_groupownerroot_permissions_syslibrary_files_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerroot_permissions_syslibrary_files_0:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib^.*$oval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_0_0:ste:1

Testing group ownership of /lib64/  oval:ssg-test_file_groupownerroot_permissions_syslibrary_files_1:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerroot_permissions_syslibrary_files_1:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/lib64^.*$oval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_0_0:ste:1

Testing group ownership of /usr/lib/  oval:ssg-test_file_groupownerroot_permissions_syslibrary_files_2:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerroot_permissions_syslibrary_files_2:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib^.*$oval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_0_0:ste:1

Testing group ownership of /usr/lib64/  oval:ssg-test_file_groupownerroot_permissions_syslibrary_files_3:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_groupownerroot_permissions_syslibrary_files_3:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
no value/usr/lib64^.*$oval:ssg-symlink_file_groupowner:ste:1oval:ssg-state_file_groupownerroot_permissions_syslibrary_files_0_0:ste:1
Ensure rootfiles tmpfile.d is Configured Correctlyxccdf_org.ssgproject.content_rule_rootfiles_configured mediumCCE-86473-6

Ensure rootfiles tmpfile.d is Configured Correctly

Rule IDxccdf_org.ssgproject.content_rule_rootfiles_configured
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-rootfiles_configured:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-86473-6

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010770
stigrefSV-230325r1017136_rule
Description
To set the mode of the root user initialization file /root/.bash_profile, ensure the following lines are is included in a file ending in .conf under /etc/tmpfiles.d/.
    C /root/.bash_logout   600 root root - /usr/share/rootfiles/.bash_logout
    C /root/.bash_profile  600 root root - /usr/share/rootfiles/.bash_profile
    C /root/.bashrc        600 root root - /usr/share/rootfiles/.bashrc
    C /root/.cshrc         600 root root - /usr/share/rootfiles/.cshrc
    C /root/.tcshrc        600 root root - /usr/share/rootfiles/.tcshrc
Rationale
Local initialization files are used to configure the user's shell environment upon logon. Malicious modification of these files could compromise accounts upon logon.

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q rootfiles; then

find "/etc/tmpfiles.d/" -name "*.conf" -print0 | xargs -0 sed -i  "/C[[:space:]]*\/root\/.bash_logout/d"
    find "/etc/tmpfiles.d/" -name "*.conf" -print0 | xargs -0 sed -i  "/C[[:space:]]*\/root\/.bash_profile/d"
    find "/etc/tmpfiles.d/" -name "*.conf" -print0 | xargs -0 sed -i  "/C[[:space:]]*\/root\/.bashrc/d"
    find "/etc/tmpfiles.d/" -name "*.conf" -print0 | xargs -0 sed -i  "/C[[:space:]]*\/root\/.cshrc/d"
    find "/etc/tmpfiles.d/" -name "*.conf" -print0 | xargs -0 sed -i  "/C[[:space:]]*\/root\/.tcshrc/d"


cat << 'EOF' > /etc/tmpfiles.d/rootfiles.conf
C /root/.bash_logout 600 root root - /usr/share/rootfiles/.bash_logout
C /root/.bash_profile 600 root root - /usr/share/rootfiles/.bash_profile
C /root/.bashrc 600 root root - /usr/share/rootfiles/.bashrc
C /root/.cshrc 600 root root - /usr/share/rootfiles/.cshrc
C /root/.tcshrc 600 root root - /usr/share/rootfiles/.tcshrc

EOF

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Find configation files
  ansible.builtin.find:
    paths: /etc/tmpfiles.d/
    file_type: file
    patterns: '*.conf'
  register: rootfiles_configured_bash_logout_found_files
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Remove existing configuration
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^C\s+/root/\.bash_logout.+$
    state: absent
  loop: '{{ rootfiles_configured_bash_logout_found_files.files }}'
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly
  ansible.builtin.lineinfile:
    path: /etc/tmpfiles.d/rootfiles.conf
    create: true
    regexp: (?i)/usr/share/rootfiles/.bash_logout
    line: C /root/.bash_logout 600 root root - /usr/share/rootfiles/.bash_logout
    state: present
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Find configation files
  ansible.builtin.find:
    paths: /etc/tmpfiles.d/
    file_type: file
    patterns: '*.conf'
  register: rootfiles_configured_bash_profile_found_files
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Remove existing configuration
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^C\s+/root/\.bash_profile.+$
    state: absent
  loop: '{{ rootfiles_configured_bash_profile_found_files.files }}'
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly
  ansible.builtin.lineinfile:
    path: /etc/tmpfiles.d/rootfiles.conf
    create: true
    regexp: (?i)/usr/share/rootfiles/.bash_profile
    line: C /root/.bash_profile 600 root root - /usr/share/rootfiles/.bash_profile
    state: present
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Find configation files
  ansible.builtin.find:
    paths: /etc/tmpfiles.d/
    file_type: file
    patterns: '*.conf'
  register: rootfiles_configured_bashrc_found_files
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Remove existing configuration
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^C\s+/root/\.bashrc.+$
    state: absent
  loop: '{{ rootfiles_configured_bashrc_found_files.files }}'
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly
  ansible.builtin.lineinfile:
    path: /etc/tmpfiles.d/rootfiles.conf
    create: true
    regexp: (?i)/usr/share/rootfiles/.bashrc
    line: C /root/.bashrc 600 root root - /usr/share/rootfiles/.bashrc
    state: present
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Find configation files
  ansible.builtin.find:
    paths: /etc/tmpfiles.d/
    file_type: file
    patterns: '*.conf'
  register: rootfiles_configured_cshrc_found_files
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Remove existing configuration
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^C\s+/root/\.cshrc.+$
    state: absent
  loop: '{{ rootfiles_configured_cshrc_found_files.files }}'
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly
  ansible.builtin.lineinfile:
    path: /etc/tmpfiles.d/rootfiles.conf
    create: true
    regexp: (?i)/usr/share/rootfiles/.cshrc
    line: C /root/.cshrc 600 root root - /usr/share/rootfiles/.cshrc
    state: present
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Find configation files
  ansible.builtin.find:
    paths: /etc/tmpfiles.d/
    file_type: file
    patterns: '*.conf'
  register: rootfiles_configured_tcshrc_found_files
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly - Remove existing configuration
  ansible.builtin.lineinfile:
    path: '{{ item.path }}'
    regexp: ^C\s+/root/\.tcshrc.+$
    state: absent
  loop: '{{ rootfiles_configured_tcshrc_found_files.files }}'
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured

- name: Ensure rootfiles tmpfile.d is Configured Correctly
  ansible.builtin.lineinfile:
    path: /etc/tmpfiles.d/rootfiles.conf
    create: true
    regexp: (?i)/usr/share/rootfiles/.tcshrc
    line: C /root/.tcshrc 600 root root - /usr/share/rootfiles/.tcshrc
    state: present
  when: '"rootfiles" in ansible_facts.packages'
  tags:
  - CCE-86473-6
  - DISA-STIG-RHEL-08-010770
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - rootfiles_configured
OVAL test results details

Tests that .bash_logout is configured correctly.  oval:ssg-test_rootfiles_configured_bash_logout:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rootfiles_configured_bash_logout:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/tmpfiles.d/^.*\.conf$^C[[:blank:]]+\/root\/.bash_logout[[:blank:]]+(\d{3})[[:blank:]]+root[[:blank:]]+root[[:blank:]]+-[[:blank:]]+\/usr\/share\/rootfiles/.bash_logout$1

Tests that .bash_profile is configured correctly.  oval:ssg-test_rootfiles_configured_bash_profile:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rootfiles_configured_bash_profile:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/tmpfiles.d/^.*\.conf$^C[[:blank:]]+\/root\/.bash_profile[[:blank:]]+(\d{3})[[:blank:]]+root[[:blank:]]+root[[:blank:]]+-[[:blank:]]+\/usr\/share\/rootfiles/.bash_profile$1

Tests that .bashrc is configured correctly.  oval:ssg-test_rootfiles_configured_bashrc:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rootfiles_configured_bashrc:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/tmpfiles.d/^.*\.conf$^C[[:blank:]]+\/root\/.bashrc[[:blank:]]+(\d{3})[[:blank:]]+root[[:blank:]]+root[[:blank:]]+-[[:blank:]]+\/usr\/share\/rootfiles/.bashrc$1

Tests that .cshrc is configured correctly.  oval:ssg-test_rootfiles_configured_cshrc:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rootfiles_configured_cshrc:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/tmpfiles.d/^.*\.conf$^C[[:blank:]]+\/root\/.cshrc[[:blank:]]+(\d{3})[[:blank:]]+root[[:blank:]]+root[[:blank:]]+-[[:blank:]]+\/usr\/share\/rootfiles/.cshrc$1

Tests that .tcshrc is configured correctly.  oval:ssg-test_rootfiles_configured_tcshrc:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_rootfiles_configured_tcshrc:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/tmpfiles.d/^.*\.conf$^C[[:blank:]]+\/root\/.tcshrc[[:blank:]]+(\d{3})[[:blank:]]+root[[:blank:]]+root[[:blank:]]+-[[:blank:]]+\/usr\/share\/rootfiles/.tcshrc$1
Ensure All World-Writable Directories Are Owned by root Userxccdf_org.ssgproject.content_rule_dir_perms_world_writable_root_owned mediumCCE-83375-6

Ensure All World-Writable Directories Are Owned by root User

Rule IDxccdf_org.ssgproject.content_rule_dir_perms_world_writable_root_owned
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_perms_world_writable_root_owned:def:1
Time2025-11-18T17:05:39-05:00
Severitymedium
Identifiers:

CCE-83375-6

References:
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000138-GPOS-00069
anssiR54
stigidRHEL-08-010700
stigrefSV-230318r1017129_rule
Description
All directories in local partitions which are world-writable should be owned by root. If any world-writable directories are not owned by root, this should be investigated. Following this, the files should be deleted or assigned to root user.
Rationale
Allowing a user account to own a world-writable directory is undesirable because it allows the owner of that directory to remove or replace any files that may be placed in the directory by other users.
OVAL test results details

check for local directories that are world writable and have uid greater than 0  oval:ssg-test_dir_world_writable_uid_gt_zero:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-all_local_directories_uid_zero:obj:1 of type file_object
BehaviorsPathFilenameFilter
no value/no valueoval:ssg-state_uid_is_not_root_and_world_writable:ste:1
Verify that All World-Writable Directories Have Sticky Bits Setxccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits mediumCCE-80783-4

Verify that All World-Writable Directories Have Sticky Bits Set

Rule IDxccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_perms_world_writable_sticky_bits:def:1
Time2025-11-18T17:05:41-05:00
Severitymedium
Identifiers:

CCE-80783-4

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000138-GPOS-00069
anssiR54
cis6.1.11
pcidss42.2.6, 2.2
stigidRHEL-08-010190
stigrefSV-230243r1069294_rule
Description
When the so-called 'sticky bit' is set on a directory, only the owner of a given file may remove that file from the directory. Without the sticky bit, any user with write access to a directory may remove any file in the directory. Setting the sticky bit prevents users from removing each other's files. In cases where there is no reason for a directory to be world-writable, a better solution is to remove that permission rather than to set the sticky bit. However, if a directory is used by a particular application, consult that application's documentation instead of blindly changing modes.
To set the sticky bit on a world-writable directory DIR, run the following command:
$ sudo chmod +t DIR
Rationale
Failing to set the sticky bit on public directories allows unauthorized users to delete files in the directory structure.

The only authorized public directories are those temporary directories supplied with the system, or those designed to be temporary file repositories. The setting is normally reserved for directories used by the system, by users for temporary file storage (such as /tmp), and for directories requiring global read/write access.
Warnings
warning  This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of directories present on the system. It is not a problem in most cases, but especially systems with a large number of directories can be affected. See https://access.redhat.com/articles/6999111.
warning  Please note that there might be cases where the rule remediation cannot fix directory permissions. This can happen for example when running on a system with some immutable parts. These immutable parts cannot be remediated because they are read-only. Example of such directories can be OStree deployments located at /sysroot/ostree/deploy. In such case, it is needed to make modifications to the underlying ostree snapshot and this is out of scope of regular rule remediation.
OVAL test results details

Check the existence of world-writable directories without sticky bits  oval:ssg-test_dir_perms_world_writable_sticky_bits:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_dir_perms_world_writable_sticky_bits:obj:1 of type file_object
BehaviorsPathFilenameFilter
/
/boot
/run/media/admin/RHEL-8-10-0-BaseOS-x86_64
/home
no valueno valueoval:ssg-state_dir_perms_world_writable_sticky_bits:ste:1
Ensure All World-Writable Directories Are Group Owned by a System Accountxccdf_org.ssgproject.content_rule_dir_perms_world_writable_system_owned_group mediumCCE-85886-0

Ensure All World-Writable Directories Are Group Owned by a System Account

Rule IDxccdf_org.ssgproject.content_rule_dir_perms_world_writable_system_owned_group
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-dir_perms_world_writable_system_owned_group:def:1
Time2025-11-18T17:05:43-05:00
Severitymedium
Identifiers:

CCE-85886-0

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010710
stigrefSV-230319r1017130_rule
Description
All directories in local partitions which are world-writable should be group owned by root or another system account. If any world-writable directories are not group owned by a system account, this should be investigated. Following this, the files should be deleted or assigned to an appropriate group.
Rationale
Allowing a user account to group own a world-writable directory is undesirable because it allows the owner of that directory to remove or replace any files that may be placed in the directory by other users.
OVAL test results details

check for local directories that are world writable and have gid greater than or equal to 1000  oval:ssg-test_dir_world_writable_gid_gt_value:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-all_local_directories_gid:obj:1 of type file_object
BehaviorsPathFilenameFilter
no value/no valueoval:ssg-state_gid_is_user_and_world_writable:ste:1
Ensure All Files Are Owned by a Groupxccdf_org.ssgproject.content_rule_file_permissions_ungroupowned mediumCCE-83497-8

Ensure All Files Are Owned by a Group

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_ungroupowned
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_ungroupowned:def:1
Time2025-11-18T17:06:18-05:00
Severitymedium
Identifiers:

CCE-83497-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.06, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1)
nist-csfPR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
anssiR53
cis6.1.12
pcidss42.2.6, 2.2
stigidRHEL-08-010790
stigrefSV-230327r1069285_rule
Description
If any file is not group-owned by a valid defined group, the cause of the lack of group-ownership must be investigated. Following this, those files should be deleted or assigned to an appropriate group. The groups need to be defined in /etc/group or in /usr/lib/group if nss-altfiles are configured to be used in /etc/nsswitch.conf. Locate the mount points related to local devices by the following command:
$ findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,)
For all mount points listed by the previous command, it is necessary to search for files which do not belong to a valid group using the following command:
$ sudo find MOUNTPOINT -xdev -nogroup 2>/dev/null
Rationale
Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account, or other similar cases. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.
Warnings
warning  This rule only considers local groups as valid groups. If you have your groups defined outside /etc/group or /usr/lib/group, the rule won't consider those.
warning  This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of files present on the system. It is not a problem in most cases, but especially systems with a large number of files can be affected. See https://access.redhat.com/articles/6999111.
OVAL test results details

Test if /etc/nssswitch.conf contains 'altfiles' in 'group' key  oval:ssg-test_file_permissions_ungroupowned_nsswitch_uses_altfiles:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/nsswitch.confgroup: files sss systemd

package nss-altfiles is installed  oval:ssg-test_file_permissions_ungroupowned_package_nss-altfiles_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_file_permissions_ungroupowned_package_nss-altfiles_installed:obj:1 of type rpminfo_object
Name
nss-altfiles

there are no files with group owner different than local groups  oval:ssg-test_file_permissions_ungroupowned:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_ungroupowned:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
/
/boot
/run/media/admin/RHEL-8-10-0-BaseOS-x86_64
/home
1
0
3
4
5
6
7
8
9
10
11
12
15
18
19
20
33
39
50
54
63
100
65534
81
22
35
999
36
998
190
997
193
59
996
995
994
993
992
2
172
991
990
989
171
988
107
987
113
986
32
985
984
983
76
982
981
980
979
978
977
976
29
42
975
74
70
21
72
1000
no value.*oval:ssg-state_file_permissions_ungroupowned_local_group_owner:ste:1oval:ssg-state_file_permissions_ungroupowned_sysroot:ste:1

Test if /etc/nssswitch.conf contains 'altfiles' in 'group' key  oval:ssg-test_file_permissions_ungroupowned_nsswitch_uses_altfiles:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/nsswitch.confgroup: files sss systemd

package nss-altfiles is installed  oval:ssg-test_file_permissions_ungroupowned_package_nss-altfiles_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_file_permissions_ungroupowned_package_nss-altfiles_installed:obj:1 of type rpminfo_object
Name
nss-altfiles

there are no files with group owner different than local groups  oval:ssg-test_file_permissions_ungroupowned_with_usrlib:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_ungroupowned_with_usrlib:obj:1 of type file_object
BehaviorsPathFilenameFilterFilter
/
/boot
/run/media/admin/RHEL-8-10-0-BaseOS-x86_64
/home
1
0
3
4
5
6
7
8
9
10
11
12
15
18
19
20
33
39
50
54
63
100
65534
81
22
35
999
36
998
190
997
193
59
996
995
994
993
992
2
172
991
990
989
171
988
107
987
113
986
32
985
984
983
76
982
981
980
979
978
977
976
29
42
975
74
70
21
72
1000
no value.*oval:ssg-state_file_permissions_ungroupowned_local_group_owner_with_usrlib:ste:1oval:ssg-state_file_permissions_ungroupowned_sysroot:ste:1
Ensure All Files Are Owned by a Userxccdf_org.ssgproject.content_rule_no_files_unowned_by_user mediumCCE-83499-4

Ensure All Files Are Owned by a User

Rule IDxccdf_org.ssgproject.content_rule_no_files_unowned_by_user
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-no_files_unowned_by_user:def:1
Time2025-11-18T17:06:34-05:00
Severitymedium
Identifiers:

CCE-83499-4

References:
cis-csc11, 12, 13, 14, 15, 16, 18, 3, 5, 9
cobit5APO01.06, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.AC-6, PR.DS-5, PR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
anssiR53
cis6.1.12
pcidss42.2.6, 2.2
stigidRHEL-08-010780
stigrefSV-230326r1069284_rule
Description
If any files are not owned by a user, then the cause of their lack of ownership should be investigated. Following this, the files should be deleted or assigned to an appropriate user. Locate the mount points related to local devices by the following command:
$ findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,)
For all mount points listed by the previous command, it is necessary to search for files which do not belong to a valid user using the following command:
$ sudo find MOUNTPOINT -xdev -nouser 2>/dev/null
Rationale
Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account, or other similar cases. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.
Warnings
warning  For this rule to evaluate centralized user accounts, getent must be working properly so that running the command
getent passwd
returns a list of all users in your organization. If using the System Security Services Daemon (SSSD),
enumerate = true
must be configured in your organization's domain to return a complete list of users
warning  This rule can take a long time to perform the check and might consume a considerable amount of resources depending on the number of files present on the system. It is not a problem in most cases, but especially systems with a large number of files can be affected. See https://access.redhat.com/articles/6999111.
OVAL test results details

there are no files without a known owner  oval:ssg-test_no_files_unowned_by_user:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_files_unowned_by_user:obj:1 of type file_object
BehaviorsPathFilenameFilter
4
5
2
12
14
65534
81
999
193
59
998
997
996
172
995
0
6
7
3
8
11
1
171
988
107
987
113
986
32
985
984
983
982
981
980
979
978
977
29
42
976
74
70
72
1000
/boot
/run/media/admin/RHEL-8-10-0-BaseOS-x86_64
/
/home
no value.*oval:ssg-state_no_files_unowned_by_user_uids_list:ste:1
Disable the Automounterxccdf_org.ssgproject.content_rule_service_autofs_disabled mediumCCE-80873-3

Disable the Automounter

Rule IDxccdf_org.ssgproject.content_rule_service_autofs_disabled
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80873-3

References:
cis-csc1, 12, 15, 16, 5
cobit5APO13.01, DSS01.04, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.4.6
hipaa164.308(a)(3)(i), 164.308(a)(3)(ii)(A), 164.310(d)(1), 164.310(d)(2), 164.312(a)(1), 164.312(a)(2)(iv), 164.312(b)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.6
iso27001-2013A.11.2.6, A.13.1.1, A.13.2.1, A.18.1.4, A.6.2.1, A.6.2.2, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-7(a), CM-7(b), CM-6(a), MP-7
nist-csfPR.AC-1, PR.AC-3, PR.AC-6, PR.AC-7
os-srgSRG-OS-000114-GPOS-00059, SRG-OS-000378-GPOS-00163, SRG-OS-000480-GPOS-00227
cis2.2.1
stigidRHEL-08-040070
stigrefSV-230502r1017284_rule
Description
The autofs daemon mounts and unmounts filesystems, such as user home directories shared via NFS, on demand. In addition, autofs can be used to handle removable media, and the default configuration provides the cdrom device as /misc/cd. However, this method of providing access to removable media is not common, so autofs can almost always be disabled if NFS is not in use. Even if NFS is required, it may be possible to configure filesystem mounts statically by editing /etc/fstab rather than relying on the automounter.

The autofs service can be disabled with the following command:
$ sudo systemctl mask --now autofs.service
Rationale
Disabling the automounter permits the administrator to statically control filesystem mounting through /etc/fstab.

Additionally, automatically mounting filesystems permits easy introduction of unknown devices, thereby facilitating malicious activity.
Disable Mounting of cramfsxccdf_org.ssgproject.content_rule_kernel_module_cramfs_disabled lowCCE-81031-7

Disable Mounting of cramfs

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_cramfs_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_cramfs_disabled:def:1
Time2025-11-18T17:07:01-05:00
Severitylow
Identifiers:

CCE-81031-7

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
cui3.4.6
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000095-GPOS-00049
cis1.1.1.1
stigidRHEL-08-040025
stigrefSV-230498r1069314_rule
Description
To configure the system to prevent the cramfs kernel module from being loaded, add the following line to the file /etc/modprobe.d/cramfs.conf:
install cramfs /bin/false
To configure the system to prevent the cramfs from being used, add the following line to file /etc/modprobe.d/cramfs.conf:
blacklist cramfs
This effectively prevents usage of this uncommon filesystem. The cramfs filesystem type is a compressed read-only Linux filesystem embedded in small footprint systems. A cramfs image can be used without having to first decompress the image.
Rationale
Removing support for unneeded filesystem types reduces the local attack surface of the server.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install cramfs" /etc/modprobe.d/cramfs.conf ; then
	
	sed -i 's#^install cramfs.*#install cramfs /bin/false#g' /etc/modprobe.d/cramfs.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cramfs.conf
	echo "install cramfs /bin/false" >> /etc/modprobe.d/cramfs.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist cramfs$" /etc/modprobe.d/cramfs.conf ; then
	echo "blacklist cramfs" >> /etc/modprobe.d/cramfs.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81031-7
  - DISA-STIG-RHEL-08-040025
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_cramfs_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'cramfs' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/cramfs.conf
    regexp: install\s+cramfs
    line: install cramfs /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81031-7
  - DISA-STIG-RHEL-08-040025
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_cramfs_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

- name: Ensure kernel module 'cramfs' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/cramfs.conf
    regexp: ^blacklist cramfs$
    line: blacklist cramfs
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81031-7
  - DISA-STIG-RHEL-08-040025
  - NIST-800-171-3.4.6
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - kernel_module_cramfs_disabled
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20cramfs%20/bin/false%0Ablacklist%20cramfs%0A
        mode: 0644
        path: /etc/modprobe.d/cramfs.conf
        overwrite: true
OVAL test results details

kernel module cramfs blacklisted  oval:ssg-test_kernmod_cramfs_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_cramfs_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+cramfs$1

kernel module cramfs disabled  oval:ssg-test_kernmod_cramfs_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_cramfs_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+cramfs\s+(/bin/false|/bin/true)$1
Disable Modprobe Loading of USB Storage Driverxccdf_org.ssgproject.content_rule_kernel_module_usb-storage_disabled mediumCCE-80835-2

Disable Modprobe Loading of USB Storage Driver

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_usb-storage_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_usb-storage_disabled:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80835-2

References:
cis-csc1, 12, 15, 16, 5
cobit5APO13.01, DSS01.04, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.1.21
hipaa164.308(a)(3)(i), 164.308(a)(3)(ii)(A), 164.310(d)(1), 164.310(d)(2), 164.312(a)(1), 164.312(a)(2)(iv), 164.312(b)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.6
iso27001-2013A.11.2.6, A.13.1.1, A.13.2.1, A.18.1.4, A.6.2.1, A.6.2.2, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-7(a), CM-7(b), CM-6(a), MP-7
nist-csfPR.AC-1, PR.AC-3, PR.AC-6, PR.AC-7
os-srgSRG-OS-000114-GPOS-00059, SRG-OS-000378-GPOS-00163, SRG-OS-000480-GPOS-00227
app-srg-ctrSRG-APP-000141-CTR-000315
cis1.1.1.8
pcidss43.4.2, 3.4
stigidRHEL-08-040080
stigrefSV-230503r1069316_rule
Description
To prevent USB storage devices from being used, configure the kernel module loading system to prevent automatic loading of the USB storage driver. To configure the system to prevent the usb-storage kernel module from being loaded, add the following line to the file /etc/modprobe.d/usb-storage.conf:
install usb-storage /bin/false
To configure the system to prevent the usb-storage from being used, add the following line to file /etc/modprobe.d/usb-storage.conf:
blacklist usb-storage
This will prevent the modprobe program from loading the usb-storage module, but will not prevent an administrator (or another program) from using the insmod program to load the module manually.
Rationale
USB storage devices such as thumb drives can be used to introduce malicious software.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install usb-storage" /etc/modprobe.d/usb-storage.conf ; then
	
	sed -i 's#^install usb-storage.*#install usb-storage /bin/false#g' /etc/modprobe.d/usb-storage.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/usb-storage.conf
	echo "install usb-storage /bin/false" >> /etc/modprobe.d/usb-storage.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist usb-storage$" /etc/modprobe.d/usb-storage.conf ; then
	echo "blacklist usb-storage" >> /etc/modprobe.d/usb-storage.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80835-2
  - DISA-STIG-RHEL-08-040080
  - NIST-800-171-3.1.21
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - PCI-DSSv4-3.4
  - PCI-DSSv4-3.4.2
  - disable_strategy
  - kernel_module_usb-storage_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'usb-storage' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/usb-storage.conf
    regexp: install\s+usb-storage
    line: install usb-storage /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80835-2
  - DISA-STIG-RHEL-08-040080
  - NIST-800-171-3.1.21
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - PCI-DSSv4-3.4
  - PCI-DSSv4-3.4.2
  - disable_strategy
  - kernel_module_usb-storage_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'usb-storage' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/usb-storage.conf
    regexp: ^blacklist usb-storage$
    line: blacklist usb-storage
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80835-2
  - DISA-STIG-RHEL-08-040080
  - NIST-800-171-3.1.21
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - PCI-DSSv4-3.4
  - PCI-DSSv4-3.4.2
  - disable_strategy
  - kernel_module_usb-storage_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20usb-storage%20/bin/false%0Ablacklist%20usb-storage%0A
        mode: 0644
        path: /etc/modprobe.d/usb-storage.conf
        overwrite: true
OVAL test results details

kernel module usb-storage blacklisted  oval:ssg-test_kernmod_usb-storage_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_usb-storage_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+usb-storage$1

kernel module usb-storage disabled  oval:ssg-test_kernmod_usb-storage_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_usb-storage_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+usb-storage\s+(/bin/false|/bin/true)$1
Add nosuid Option to /boot/efixccdf_org.ssgproject.content_rule_mount_option_boot_efi_nosuid mediumCCE-86038-7

Add nosuid Option to /boot/efi

Rule IDxccdf_org.ssgproject.content_rule_mount_option_boot_efi_nosuid
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-86038-7

References:
nistCM-6(b), CM-6.1(iv)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010572
stigrefSV-244530r1017337_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /boot/efi. The SUID and SGID permissions should not be required on the boot partition. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /boot/efi.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from boot partitions.
Add nosuid Option to /bootxccdf_org.ssgproject.content_rule_mount_option_boot_nosuid mediumCCE-81033-3

Add nosuid Option to /boot

Rule IDxccdf_org.ssgproject.content_rule_mount_option_boot_nosuid
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_boot_nosuid:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-81033-3

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00227
anssiR28
stigidRHEL-08-010571
stigrefSV-230300r1017110_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /boot. The SUID and SGID permissions should not be required on the boot partition. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /boot.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from boot partitions.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

function perform_remediation {
    
        # the mount point /boot has to be defined in /etc/fstab
        # before this remediation can be executed. In case it is not defined, the
        # remediation aborts and no changes regarding the mount point are done.
        mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" "/boot")"

    grep "$mount_point_match_regexp" -q /etc/fstab \
        || { echo "The mount point '/boot' is not even in /etc/fstab, so we can't set up mount options" >&2;
                echo "Not remediating, because there is no record of /boot in /etc/fstab" >&2; return 1; }
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /boot)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type=""
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo " /boot  defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nosuid"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab
    fi


    if mkdir -p "/boot"; then
        if mountpoint -q "/boot"; then
            mount -o remount --target "/boot"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /boot: Check information associated to mountpoint'
  command: findmnt --fstab '/boot'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /boot: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /boot: If /boot not mounted, craft mount_info manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /boot
    - ''
    - ''
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - ("--fstab" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /boot: Make sure nosuid option is part of the to /boot
    options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined and "nosuid" not in mount_info.options
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /boot: Ensure /boot is mounted with nosuid option'
  mount:
    path: /boot
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab"
    | length == 0)
  tags:
  - CCE-81033-3
  - DISA-STIG-RHEL-08-010571
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_boot_nosuid
  - no_reboot_needed

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /boot --mountoptions="nosuid"
OVAL test results details

nosuid on /boot   oval:ssg-test_boot_partition_nosuid_optional:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
false/boot/dev/sda1a347087d-210e-4b21-9925-083eb6e532ffxfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind25958469704189880

/boot exists  oval:ssg-test_boot_partition_nosuid_optional_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/boot/dev/sda1a347087d-210e-4b21-9925-083eb6e532ffxfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind25958469704189880

nosuid on /boot in /etc/fstab  oval:ssg-test_boot_partition_nosuid_optional_in_fstab:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/fstabUUID=a347087d-210e-4b21-9925-083eb6e532ff /boot xfs defaults

/boot exists in /etc/fstab  oval:ssg-test_boot_partition_nosuid_optional_exist_in_fstab:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/fstabUUID=a347087d-210e-4b21-9925-083eb6e532ff /boot xfs defaults
Add nodev Option to /dev/shmxccdf_org.ssgproject.content_rule_mount_option_dev_shm_nodev mediumCCE-80837-8

Add nodev Option to /dev/shm

Rule IDxccdf_org.ssgproject.content_rule_mount_option_dev_shm_nodev
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_dev_shm_nodev:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80837-8

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.2.2
stigidRHEL-08-040120
stigrefSV-230508r958804_rule
Description
The nodev mount option can be used to prevent creation of device files in /dev/shm. Legitimate character and block devices should not exist within temporary directories like /dev/shm. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

function perform_remediation {
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type="tmpfs"
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nodev"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab
    fi


    if mkdir -p "/dev/shm"; then
        if mountpoint -q "/dev/shm"; then
            mount -o remount --target "/dev/shm"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed

- name: 'Add nodev Option to /dev/shm: Check information associated to mountpoint'
  command: findmnt  '/dev/shm'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed

- name: 'Add nodev Option to /dev/shm: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed

- name: 'Add nodev Option to /dev/shm: If /dev/shm not mounted, craft mount_info manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /dev/shm
    - tmpfs
    - tmpfs
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - ("" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed

- name: 'Add nodev Option to /dev/shm: Make sure nodev option is part of the to /dev/shm
    options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nodev''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined and "nodev" not in mount_info.options
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed

- name: 'Add nodev Option to /dev/shm: Ensure /dev/shm is mounted with nodev option'
  mount:
    path: /dev/shm
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" |
    length == 0)
  tags:
  - CCE-80837-8
  - DISA-STIG-RHEL-08-040120
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nodev
  - no_reboot_needed
OVAL test results details

nodev on /dev/shm   oval:ssg-test_dev_shm_partition_nodev_expected:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
true/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

/dev/shm exists  oval:ssg-test_dev_shm_partition_nodev_expected_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

nodev on /dev/shm in /etc/fstab  oval:ssg-test_dev_shm_partition_nodev_expected_in_fstab:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_dev_shm_partition_nodev_expected_in_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fstab^[\s]*(?!#)[\S]+[\s]+/dev/shm[\s]+[\S]+[\s]+([\S]+)1
Add noexec Option to /dev/shmxccdf_org.ssgproject.content_rule_mount_option_dev_shm_noexec mediumCCE-80838-6

Add noexec Option to /dev/shm

Rule IDxccdf_org.ssgproject.content_rule_mount_option_dev_shm_noexec
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_dev_shm_noexec:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80838-6

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.2.4
stigidRHEL-08-040122
stigrefSV-230510r958804_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /dev/shm. It can be dangerous to allow the execution of binaries from world-writable temporary storage directories such as /dev/shm. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.
Rationale
Allowing users to execute binaries from world-writable directories such as /dev/shm can expose the system to potential compromise.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

function perform_remediation {
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type="tmpfs"
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "noexec"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab
    fi


    if mkdir -p "/dev/shm"; then
        if mountpoint -q "/dev/shm"; then
            mount -o remount --target "/dev/shm"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /dev/shm: Check information associated to mountpoint'
  command: findmnt  '/dev/shm'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /dev/shm: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /dev/shm: If /dev/shm not mounted, craft mount_info
    manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /dev/shm
    - tmpfs
    - tmpfs
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - ("" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /dev/shm: Make sure noexec option is part of the to
    /dev/shm options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined and "noexec" not in mount_info.options
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /dev/shm: Ensure /dev/shm is mounted with noexec option'
  mount:
    path: /dev/shm
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" |
    length == 0)
  tags:
  - CCE-80838-6
  - DISA-STIG-RHEL-08-040122
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_noexec
  - no_reboot_needed
OVAL test results details

noexec on /dev/shm   oval:ssg-test_dev_shm_partition_noexec_expected:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
false/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

/dev/shm exists  oval:ssg-test_dev_shm_partition_noexec_expected_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

noexec on /dev/shm in /etc/fstab  oval:ssg-test_dev_shm_partition_noexec_expected_in_fstab:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_dev_shm_partition_noexec_expected_in_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fstab^[\s]*(?!#)[\S]+[\s]+/dev/shm[\s]+[\S]+[\s]+([\S]+)1
Add nosuid Option to /dev/shmxccdf_org.ssgproject.content_rule_mount_option_dev_shm_nosuid mediumCCE-80839-4

Add nosuid Option to /dev/shm

Rule IDxccdf_org.ssgproject.content_rule_mount_option_dev_shm_nosuid
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_dev_shm_nosuid:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80839-4

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.2.3
stigidRHEL-08-040121
stigrefSV-230509r958804_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /dev/shm. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

function perform_remediation {
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type="tmpfs"
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nosuid"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab
    fi


    if mkdir -p "/dev/shm"; then
        if mountpoint -q "/dev/shm"; then
            mount -o remount --target "/dev/shm"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /dev/shm: Check information associated to mountpoint'
  command: findmnt  '/dev/shm'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /dev/shm: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /dev/shm: If /dev/shm not mounted, craft mount_info
    manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /dev/shm
    - tmpfs
    - tmpfs
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - ("" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /dev/shm: Make sure nosuid option is part of the to
    /dev/shm options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined and "nosuid" not in mount_info.options
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /dev/shm: Ensure /dev/shm is mounted with nosuid option'
  mount:
    path: /dev/shm
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("" |
    length == 0)
  tags:
  - CCE-80839-4
  - DISA-STIG-RHEL-08-040121
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_dev_shm_nosuid
  - no_reboot_needed
OVAL test results details

nosuid on /dev/shm   oval:ssg-test_dev_shm_partition_nosuid_expected:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
true/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

/dev/shm exists  oval:ssg-test_dev_shm_partition_nosuid_expected_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/dev/shmtmpfstmpfsrwseclabelnosuidnodev201741402017414

nosuid on /dev/shm in /etc/fstab  oval:ssg-test_dev_shm_partition_nosuid_expected_in_fstab:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_dev_shm_partition_nosuid_expected_in_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fstab^[\s]*(?!#)[\S]+[\s]+/dev/shm[\s]+[\S]+[\s]+([\S]+)1
Add noexec Option to /homexccdf_org.ssgproject.content_rule_mount_option_home_noexec mediumCCE-83328-5

Add noexec Option to /home

Rule IDxccdf_org.ssgproject.content_rule_mount_option_home_noexec
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_home_noexec:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-83328-5

References:
nistCM-6(b)
os-srgSRG-OS-000480-GPOS-00227
anssiR28
stigidRHEL-08-010590
stigrefSV-230302r1017112_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /home. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /home.
Rationale
The /home directory contains data of individual users. Binaries in this directory should not be considered as trusted and users should not be able to execute them.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

function perform_remediation {
    
        # the mount point /home has to be defined in /etc/fstab
        # before this remediation can be executed. In case it is not defined, the
        # remediation aborts and no changes regarding the mount point are done.
        mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" "/home")"

    grep "$mount_point_match_regexp" -q /etc/fstab \
        || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2;
                echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; }
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /home)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type=""
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo " /home  defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "noexec"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab
    fi


    if mkdir -p "/home"; then
        if mountpoint -q "/home"; then
            mount -o remount --target "/home"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /home: Check information associated to mountpoint'
  command: findmnt --fstab '/home'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /home: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /home: If /home not mounted, craft mount_info manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /home
    - ''
    - ''
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - ("--fstab" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /home: Make sure noexec option is part of the to /home
    options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',noexec''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined and "noexec" not in mount_info.options
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

- name: 'Add noexec Option to /home: Ensure /home is mounted with noexec option'
  mount:
    path: /home
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab"
    | length == 0)
  tags:
  - CCE-83328-5
  - DISA-STIG-RHEL-08-010590
  - NIST-800-53-CM-6(b)
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_noexec
  - no_reboot_needed

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /home --mountoptions="noexec"
OVAL test results details

noexec on /home   oval:ssg-test_home_partition_noexec_optional:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
false/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866

/home exists  oval:ssg-test_home_partition_noexec_optional_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866

noexec on /home in /etc/fstab  oval:ssg-test_home_partition_noexec_optional_in_fstab:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/fstab/dev/mapper/rhel-home /home xfs defaults

/home exists in /etc/fstab  oval:ssg-test_home_partition_noexec_optional_exist_in_fstab:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/fstab/dev/mapper/rhel-home /home xfs defaults
Add nosuid Option to /homexccdf_org.ssgproject.content_rule_mount_option_home_nosuid mediumCCE-81050-7

Add nosuid Option to /home

Rule IDxccdf_org.ssgproject.content_rule_mount_option_home_nosuid
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_home_nosuid:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-81050-7

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00227
anssiR28
cis1.1.2.3.3
stigidRHEL-08-010570
stigrefSV-230299r1017109_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /home. The SUID and SGID permissions should not be required in these user data directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /home.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from user home directory partitions.

Reboot:false
# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ) && { findmnt --kernel "/home" > /dev/null || findmnt --fstab "/home" > /dev/null; }; then

function perform_remediation {
    
        # the mount point /home has to be defined in /etc/fstab
        # before this remediation can be executed. In case it is not defined, the
        # remediation aborts and no changes regarding the mount point are done.
        mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" "/home")"

    grep "$mount_point_match_regexp" -q /etc/fstab \
        || { echo "The mount point '/home' is not even in /etc/fstab, so we can't set up mount options" >&2;
                echo "Not remediating, because there is no record of /home in /etc/fstab" >&2; return 1; }
    


    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /home)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type=""
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo " /home  defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nosuid"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab
    fi


    if mkdir -p "/home"; then
        if mountpoint -q "/home"; then
            mount -o remount --target "/home"
        fi
    fi
}

perform_remediation

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /home: Check information associated to mountpoint'
  command: findmnt --fstab '/home'
  register: device_name
  failed_when: device_name.rc > 1
  changed_when: false
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - '"/home" in ansible_mounts | map(attribute="mount") | list'
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /home: Create mount_info dictionary variable'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - '{{ device_name.stdout_lines[0].split() | list | lower }}'
  - '{{ device_name.stdout_lines[1].split() | list }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - '"/home" in ansible_mounts | map(attribute="mount") | list'
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length > 0)
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /home: If /home not mounted, craft mount_info manually'
  set_fact:
    mount_info: '{{ mount_info|default({})|combine({item.0: item.1}) }}'
  with_together:
  - - target
    - source
    - fstype
    - options
  - - /home
    - ''
    - ''
    - defaults
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - '"/home" in ansible_mounts | map(attribute="mount") | list'
  - ("--fstab" | length == 0)
  - device_name.stdout is defined and device_name.stdout_lines is defined
  - (device_name.stdout | length == 0)
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /home: Make sure nosuid option is part of the to /home
    options'
  set_fact:
    mount_info: '{{ mount_info | combine( {''options'':''''~mount_info.options~'',nosuid''
      }) }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - '"/home" in ansible_mounts | map(attribute="mount") | list'
  - mount_info is defined and "nosuid" not in mount_info.options
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

- name: 'Add nosuid Option to /home: Ensure /home is mounted with nosuid option'
  mount:
    path: /home
    src: '{{ mount_info.source }}'
    opts: '{{ mount_info.options }}'
    state: mounted
    fstype: '{{ mount_info.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - '"/home" in ansible_mounts | map(attribute="mount") | list'
  - mount_info is defined
  - (device_name.stdout is defined and (device_name.stdout | length > 0)) or ("--fstab"
    | length == 0)
  tags:
  - CCE-81050-7
  - DISA-STIG-RHEL-08-010570
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_home_nosuid
  - no_reboot_needed

Complexity:low
Disruption:high
Reboot:false
Strategy:enable

part /home --mountoptions="nosuid"
OVAL test results details

nosuid on /home   oval:ssg-test_home_partition_nosuid_optional:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
false/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866

/home exists  oval:ssg-test_home_partition_nosuid_optional_exist:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
not evaluated/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866

nosuid on /home in /etc/fstab  oval:ssg-test_home_partition_nosuid_optional_in_fstab:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/fstab/dev/mapper/rhel-home /home xfs defaults

/home exists in /etc/fstab  oval:ssg-test_home_partition_nosuid_optional_exist_in_fstab:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/fstab/dev/mapper/rhel-home /home xfs defaults
Add nodev Option to Non-Root Local Partitionsxccdf_org.ssgproject.content_rule_mount_option_nodev_nonroot_local_partitions mediumCCE-82069-6

Add nodev Option to Non-Root Local Partitions

Rule IDxccdf_org.ssgproject.content_rule_mount_option_nodev_nonroot_local_partitions
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_nodev_nonroot_local_partitions:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82069-6

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00227
anssiR28
stigidRHEL-08-010580
stigrefSV-230301r1017111_rule
Description
The nodev mount option prevents files from being interpreted as character or block devices. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of any non-root local partitions.
Rationale
The nodev mount option prevents files from being interpreted as character or block devices. The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails, for which it is not advised to set nodev on these filesystems.

# Remediation is applicable only in certain platforms
if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then

MOUNT_OPTION="nodev"
# Create array of local non-root partitions
readarray -t partitions_records < <(findmnt --mtab --raw --evaluate | grep "^/\w" | grep -v "^/proc" | grep "\s/dev/\w")

# Create array of polyinstantiated directories, in case one of them is found in mtab
readarray -t polyinstantiated_dirs < \
    <(grep -oP "^\s*[^#\s]+\s+\S+" /etc/security/namespace.conf | grep -oP "(?<=\s)\S+?(?=/?\$)")

# Define excluded non-local file systems
excluded_fstypes=(
    afs
    autofs
    ceph
    cifs
    smb3
    smbfs
    sshfs
    ncpfs
    ncp
    nfs
    nfs4
    gfs
    gfs2
    glusterfs
    gpfs
    pvfs2
    ocfs2
    lustre
    davfs
    fuse.sshfs
)

for partition_record in "${partitions_records[@]}"; do
    # Get all important information for fstab
    mount_point="$(echo "${partition_record}" | cut -d " " -f1)"
    device="$(echo "${partition_record}" | cut -d " " -f2)"
    device_type="$(echo "${partition_record}" | cut -d " " -f3)"

    # Skip polyinstantiated directories
    if printf '%s\0' "${polyinstantiated_dirs[@]}" | grep -qxzF "$mount_point"; then
        continue
    fi

    # Skip any non-local filesystem
    for excluded_fstype in "${excluded_fstypes[@]}"; do
        if [[ "$device_type" == "$excluded_fstype" ]]; then
            # jump out of both loops and move to next partition_record
            continue 2
        fi
    done

    # If we reach here, it's a local, non-root partition that isn't excluded.
    mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" $mount_point)"

    # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab
    if ! grep -q "$mount_point_match_regexp" /etc/fstab; then
        # runtime opts without some automatic kernel/userspace-added defaults
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 |  awk '{print $4}' \
                    | sed -E "s/(rw|defaults|seclabel|$MOUNT_OPTION)(,|$)//g;s/,$//")
        [ "$previous_mount_opts" ] && previous_mount_opts+=","
        # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in
        # fstab as "block".  The next variable is to satisfy shellcheck SC2050.
        fs_type="$device_type"
        if [  "$fs_type" == "iso9660" ] ; then
            previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts")
        fi
        echo "$device $mount_point $device_type defaults,${previous_mount_opts}$MOUNT_OPTION 0 0" >> /etc/fstab
    # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it
    elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "$MOUNT_OPTION"; then
        previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}')
        sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,$MOUNT_OPTION|" /etc/fstab
    fi
    if mkdir -p "$mount_point"; then
        if mountpoint -q "$mount_point"; then
            mount -o remount --target "$mount_point"
        fi
    fi
done

# Remediate unmounted /etc/fstab entries
sed -i -E '/nodev/! s;^\s*(/dev/\S+|UUID=\S+)\s+(/\w\S*)\s+(\S+)\s+(\S+)(.*)$;\1 \2 \3 \4,nodev \5;' /etc/fstab

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:high
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82069-6
  - DISA-STIG-RHEL-08-010580
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_nodev_nonroot_local_partitions
  - no_reboot_needed

- name: 'Add nodev Option to Non-Root Local Partitions: Refresh facts'
  ansible.builtin.setup:
    gather_subset: mounts
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-82069-6
  - DISA-STIG-RHEL-08-010580
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_nodev_nonroot_local_partitions
  - no_reboot_needed

- name: 'Add nodev Option to Non-Root Local Partitions: Define excluded (non-local)
    file systems'
  ansible.builtin.set_fact:
    excluded_fstypes:
    - afs
    - autofs
    - ceph
    - cifs
    - smb3
    - smbfs
    - sshfs
    - ncpfs
    - ncp
    - nfs
    - nfs4
    - gfs
    - gfs2
    - glusterfs
    - gpfs
    - pvfs2
    - ocfs2
    - lustre
    - davfs
    - fuse.sshfs
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-82069-6
  - DISA-STIG-RHEL-08-010580
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_nodev_nonroot_local_partitions
  - no_reboot_needed

- name: 'Add nodev Option to Non-Root Local Partitions: Ensure non-root local partitions
    are mounted with nodev option'
  ansible.posix.mount:
    path: '{{ item.mount }}'
    src: '{{ item.device }}'
    opts: '{{ item.options }},nodev'
    state: mounted
    fstype: '{{ item.fstype }}'
  when:
  - ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  - item.mount is match('/\w')
  - item.options is not search('nodev')
  - item.fstype not in excluded_fstypes
  with_items:
  - '{{ ansible_facts.mounts }}'
  tags:
  - CCE-82069-6
  - DISA-STIG-RHEL-08-010580
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_nodev_nonroot_local_partitions
  - no_reboot_needed

- name: 'Add nodev Option to Non-Root Local Partitions: Ensure non-root local partitions
    are present with nodev option in /etc/fstab'
  ansible.builtin.replace:
    path: /etc/fstab
    regexp: ^\s*(?!#)(/dev/\S+|UUID=\S+)\s+(/\w\S*)\s+(\S+)\s+(?!.*\bnodev\b)(\S+)(.*)$
    replace: \1 \2 \3 \4,nodev \5
  when: ( not ( "kernel" in ansible_facts.packages and "rpm-ostree" in ansible_facts.packages
    and "bootc" in ansible_facts.packages and not "openshift-kubelet" in ansible_facts.packages
    ) and not ( ansible_virtualization_type in ["docker", "lxc", "openvz", "podman",
    "container"] ) )
  tags:
  - CCE-82069-6
  - DISA-STIG-RHEL-08-010580
  - NIST-800-53-AC-6
  - NIST-800-53-AC-6(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - NIST-800-53-MP-7
  - configure_strategy
  - high_disruption
  - low_complexity
  - medium_severity
  - mount_option_nodev_nonroot_local_partitions
  - no_reboot_needed
OVAL test results details

nodev on local filesystems  oval:ssg-test_nodev_nonroot_local_partitions:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMount pointDeviceUuidFs typeMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsMount optionsTotal spaceSpace usedSpace left
true/home/dev/mapper/rhel-homec54ce016-3478-4b68-97f7-441a670054d9xfsrwseclabelrelatimeattr2inode64logbufs=8logbsize=32knoquotabind8413579459892883536866

nodev on local filesystems in /etc/fstab  oval:ssg-test_nodev_nonroot_local_partitions_in_fstab:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/fstab/dev/mapper/rhel-home /home xfs defaults
false/etc/fstabUUID=a347087d-210e-4b21-9925-083eb6e532ff /boot xfs defaults
Add nodev Option to Removable Media Partitionsxccdf_org.ssgproject.content_rule_mount_option_nodev_removable_partitions mediumCCE-82742-8

Add nodev Option to Removable Media Partitions

Rule IDxccdf_org.ssgproject.content_rule_mount_option_nodev_removable_partitions
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_nodev_removable_partitions:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82742-8

References:
cis-csc11, 12, 13, 14, 16, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.06, DSS05.07, DSS06.03, DSS06.06
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.6, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.7.1.1, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2, A.9.2.1
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.AC-3, PR.AC-6, PR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010600
stigrefSV-230303r1017113_rule
Description
The nodev mount option prevents files from being interpreted as character or block devices. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. An exception to this is chroot jails, and it is not advised to set nodev on partitions which contain their root filesystems.
OVAL test results details

Check if expected removable partitions truly exist on the system  oval:ssg-test_removable_partition_doesnt_exist:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/dev/cdromsymbolic link003rwxrwxrwx 

Check if removable partition variable value represents CD/DVD drive  oval:ssg-test_var_removable_partition_is_cd_dvd_drive:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_removable_partition:var:1/dev/cdrom

'nodev' mount option used for at least one CD / DVD drive alternative names in /etc/fstab  oval:ssg-test_nodev_etc_fstab_cd_dvd_drive:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_nodev_etc_fstab_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/dvd[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/scd0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/sr0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

'CD/DVD drive is not listed in /etc/fstab  oval:ssg-test_no_cd_dvd_drive_in_etc_fstab:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_cd_dvd_drive_in_etc_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

Check if removable partition is configured with 'nodev' mount option in /etc/fstab  oval:ssg-test_nodev_etc_fstab_not_cd_dvd_drive:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_nodev_etc_fstab_not_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/etc/fstab1
Add noexec Option to Removable Media Partitionsxccdf_org.ssgproject.content_rule_mount_option_noexec_removable_partitions mediumCCE-82746-9

Add noexec Option to Removable Media Partitions

Rule IDxccdf_org.ssgproject.content_rule_mount_option_noexec_removable_partitions
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_noexec_removable_partitions:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82746-9

References:
cis-csc11, 12, 13, 14, 16, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.06, DSS05.07, DSS06.03, DSS06.06
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.6, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.7.1.1, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2, A.9.2.1
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.AC-3, PR.AC-6, PR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010610
stigrefSV-230304r1017114_rule
Description
The noexec mount option prevents the direct execution of binaries on the mounted filesystem. Preventing the direct execution of binaries from removable media (such as a USB key) provides a defense against malicious software that may be present on such untrusted media. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.
Rationale
Allowing users to execute binaries from removable media such as USB keys exposes the system to potential compromise.
OVAL test results details

Check if expected removable partitions truly exist on the system  oval:ssg-test_removable_partition_doesnt_exist:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/dev/cdromsymbolic link003rwxrwxrwx 

Check if removable partition variable value represents CD/DVD drive  oval:ssg-test_var_removable_partition_is_cd_dvd_drive:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_removable_partition:var:1/dev/cdrom

'noexec' mount option used for at least one CD / DVD drive alternative names in /etc/fstab  oval:ssg-test_noexec_etc_fstab_cd_dvd_drive:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_noexec_etc_fstab_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/dvd[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/scd0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/sr0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

'CD/DVD drive is not listed in /etc/fstab  oval:ssg-test_no_cd_dvd_drive_in_etc_fstab:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_cd_dvd_drive_in_etc_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

Check if removable partition is configured with 'noexec' mount option in /etc/fstab  oval:ssg-test_noexec_etc_fstab_not_cd_dvd_drive:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_noexec_etc_fstab_not_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/etc/fstab1
Add nosuid Option to Removable Media Partitionsxccdf_org.ssgproject.content_rule_mount_option_nosuid_removable_partitions mediumCCE-82744-4

Add nosuid Option to Removable Media Partitions

Rule IDxccdf_org.ssgproject.content_rule_mount_option_nosuid_removable_partitions
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-mount_option_nosuid_removable_partitions:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82744-4

References:
cis-csc11, 12, 13, 14, 15, 16, 18, 3, 5, 8, 9
cobit5APO01.06, APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.06, DSS05.07, DSS06.02, DSS06.03, DSS06.06
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.11.2.6, A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.AC-3, PR.AC-4, PR.AC-6, PR.DS-5, PR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010620
stigrefSV-230305r1017115_rule
Description
The nosuid mount option prevents set-user-identifier (SUID) and set-group-identifier (SGID) permissions from taking effect. These permissions allow users to execute binaries with the same permissions as the owner and group of the file respectively. Users should not be allowed to introduce SUID and SGID files into the system via partitions mounted from removeable media. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Allowing users to introduce SUID or SGID binaries from partitions mounted off of removable media would allow them to introduce their own highly-privileged programs.
OVAL test results details

Check if expected removable partitions truly exist on the system  oval:ssg-test_removable_partition_doesnt_exist:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/dev/cdromsymbolic link003rwxrwxrwx 

Check if removable partition variable value represents CD/DVD drive  oval:ssg-test_var_removable_partition_is_cd_dvd_drive:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-var_removable_partition:var:1/dev/cdrom

'nosuid' mount option used for at least one CD / DVD drive alternative names in /etc/fstab  oval:ssg-test_nosuid_etc_fstab_cd_dvd_drive:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_nosuid_etc_fstab_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/dvd[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/scd0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
^[\s]*/dev/sr0[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

'CD/DVD drive is not listed in /etc/fstab  oval:ssg-test_no_cd_dvd_drive_in_etc_fstab:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_cd_dvd_drive_in_etc_fstab:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/dev/cdrom
/dev/dvd
/dev/scd0
/dev/sr0
/etc/fstab1

Check if removable partition is configured with 'nosuid' mount option in /etc/fstab  oval:ssg-test_nosuid_etc_fstab_not_cd_dvd_drive:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_nosuid_etc_fstab_not_cd_dvd_drive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^[\s]*/dev/cdrom[\s]+[/\w]+[\s]+[\w]+[\s]+([^\s]+)(?:[\s]+[\d]+){2}$
/dev/cdrom
/etc/fstab1
Add nodev Option to /tmpxccdf_org.ssgproject.content_rule_mount_option_tmp_nodev mediumCCE-82623-0

Add nodev Option to /tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_tmp_nodev
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82623-0

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.1.2
stigidRHEL-08-040123
stigrefSV-230511r958804_rule
Description
The nodev mount option can be used to prevent device files from being created in /tmp. Legitimate character and block devices should not exist within temporary directories like /tmp. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /tmp.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.
Add noexec Option to /tmpxccdf_org.ssgproject.content_rule_mount_option_tmp_noexec mediumCCE-82139-7

Add noexec Option to /tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_tmp_noexec
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82139-7

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.1.4
stigidRHEL-08-040125
stigrefSV-230513r958804_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /tmp. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /tmp.
Rationale
Allowing users to execute binaries from world-writable directories such as /tmp should never be necessary in normal operation and can expose the system to potential compromise.
Add nosuid Option to /tmpxccdf_org.ssgproject.content_rule_mount_option_tmp_nosuid mediumCCE-82140-5

Add nosuid Option to /tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_tmp_nosuid
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82140-5

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.1.3
stigidRHEL-08-040124
stigrefSV-230512r958804_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /tmp. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /tmp.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.
Add nodev Option to /var/log/auditxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_nodev mediumCCE-82080-3

Add nodev Option to /var/log/audit

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_nodev
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82080-3

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.7.2
stigidRHEL-08-040129
stigrefSV-230517r958804_rule
Description
The nodev mount option can be used to prevent device files from being created in /var/log/audit. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /var/log/audit.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.
Add noexec Option to /var/log/auditxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_noexec mediumCCE-82975-4

Add noexec Option to /var/log/audit

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_noexec
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82975-4

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.7.4
stigidRHEL-08-040131
stigrefSV-230519r958804_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /var/log/audit. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var/log/audit.
Rationale
Allowing users to execute binaries from directories containing audit log files such as /var/log/audit should never be necessary in normal operation and can expose the system to potential compromise.
Add nosuid Option to /var/log/auditxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_nosuid mediumCCE-82921-8

Add nosuid Option to /var/log/audit

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_audit_nosuid
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82921-8

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.7.3
stigidRHEL-08-040130
stigrefSV-230518r958804_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /var/log/audit. The SUID and SGID permissions should not be required in directories containing audit log files. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var/log/audit.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from partitions designated for audit log files.
Add nodev Option to /var/logxccdf_org.ssgproject.content_rule_mount_option_var_log_nodev mediumCCE-82077-9

Add nodev Option to /var/log

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_nodev
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82077-9

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.6.2
stigidRHEL-08-040126
stigrefSV-230514r958804_rule
Description
The nodev mount option can be used to prevent device files from being created in /var/log. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /var/log.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.
Add noexec Option to /var/logxccdf_org.ssgproject.content_rule_mount_option_var_log_noexec mediumCCE-82008-4

Add noexec Option to /var/log

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_noexec
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82008-4

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.6.4
stigidRHEL-08-040128
stigrefSV-230516r958804_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /var/log. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var/log.
Rationale
Allowing users to execute binaries from directories containing log files such as /var/log should never be necessary in normal operation and can expose the system to potential compromise.
Add nosuid Option to /var/logxccdf_org.ssgproject.content_rule_mount_option_var_log_nosuid mediumCCE-82065-4

Add nosuid Option to /var/log

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_log_nosuid
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82065-4

References:
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-7(a), CM-7(b), CM-6(a), AC-6, AC-6(1), MP-7
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.6.3
stigidRHEL-08-040127
stigrefSV-230515r958804_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /var/log. The SUID and SGID permissions should not be required in directories containing log files. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var/log.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from partitions designated for log files.
Add nodev Option to /var/tmpxccdf_org.ssgproject.content_rule_mount_option_var_tmp_nodev mediumCCE-82068-8

Add nodev Option to /var/tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_tmp_nodev
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82068-8

References:
os-srgSRG-OS-000368-GPOS-00154
cis1.1.2.5.2
stigidRHEL-08-040132
stigrefSV-230520r958804_rule
Description
The nodev mount option can be used to prevent device files from being created in /var/tmp. Legitimate character and block devices should not exist within temporary directories like /var/tmp. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /var/tmp.
Rationale
The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.
Add noexec Option to /var/tmpxccdf_org.ssgproject.content_rule_mount_option_var_tmp_noexec mediumCCE-82151-2

Add noexec Option to /var/tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_tmp_noexec
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82151-2

References:
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.5.4
stigidRHEL-08-040134
stigrefSV-230522r958804_rule
Description
The noexec mount option can be used to prevent binaries from being executed out of /var/tmp. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /var/tmp.
Rationale
Allowing users to execute binaries from world-writable directories such as /var/tmp should never be necessary in normal operation and can expose the system to potential compromise.
Add nosuid Option to /var/tmpxccdf_org.ssgproject.content_rule_mount_option_var_tmp_nosuid mediumCCE-82154-6

Add nosuid Option to /var/tmp

Rule IDxccdf_org.ssgproject.content_rule_mount_option_var_tmp_nosuid
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82154-6

References:
os-srgSRG-OS-000368-GPOS-00154
anssiR28
cis1.1.2.5.3
stigidRHEL-08-040133
stigrefSV-230521r958804_rule
Description
The nosuid mount option can be used to prevent execution of setuid programs in /var/tmp. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /var/tmp.
Rationale
The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.
Disable acquiring, saving, and processing core dumpsxccdf_org.ssgproject.content_rule_service_systemd-coredump_disabled mediumCCE-82881-4

Disable acquiring, saving, and processing core dumps

Rule IDxccdf_org.ssgproject.content_rule_service_systemd-coredump_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_systemd-coredump_disabled:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-82881-4

References:
nistSC-7(10)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010672
stigrefSV-230312r1017122_rule
Description
The systemd-coredump.socket unit is a socket activation of the systemd-coredump@.service which processes core dumps. By masking the unit, core dump processing is disabled.
Rationale
A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

SOCKET_NAME="systemd-coredump.socket"
SYSTEMCTL_EXEC='/usr/bin/systemctl'

if "$SYSTEMCTL_EXEC" -q list-unit-files --type socket | grep -q "$SOCKET_NAME"; then
    if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
      "$SYSTEMCTL_EXEC" stop "$SOCKET_NAME"
    fi
    "$SYSTEMCTL_EXEC" mask "$SOCKET_NAME"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82881-4
  - DISA-STIG-RHEL-08-010672
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_systemd-coredump_disabled

- name: Disable acquiring, saving, and processing core dumps - Collect systemd Socket
    Units Present in the System
  ansible.builtin.command:
    cmd: systemctl -q list-unit-files --type socket
  register: result_systemd_unit_files
  changed_when: false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82881-4
  - DISA-STIG-RHEL-08-010672
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_systemd-coredump_disabled

- name: Disable acquiring, saving, and processing core dumps - Ensure systemd-coredump.socket
    is Masked
  ansible.builtin.systemd:
    name: systemd-coredump.socket
    state: stopped
    enabled: false
    masked: true
  when:
  - '"kernel" in ansible_facts.packages'
  - result_systemd_unit_files.stdout_lines is search("systemd-coredump.socket")
  tags:
  - CCE-82881-4
  - DISA-STIG-RHEL-08-010672
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_systemd-coredump_disabled
OVAL test results details

Test that the property LoadState from the systemd-coredump.socket is masked  oval:ssg-test_socket_loadstate_is_masked_systemd-coredump:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsesystemd-coredump.socketLoadStateloaded
Disable core dump backtracesxccdf_org.ssgproject.content_rule_coredump_disable_backtraces mediumCCE-82251-0

Disable core dump backtraces

Rule IDxccdf_org.ssgproject.content_rule_coredump_disable_backtraces
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-coredump_disable_backtraces:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-82251-0

References:
nistCM-6
pcidssReq-3.2
os-srgSRG-OS-000480-GPOS-00227
cis1.4.3
pcidss43.3.1.1, 3.3.1, 3.3
stigidRHEL-08-010675
stigrefSV-230315r1017126_rule
Description
The ProcessSizeMax option in [Coredump] section of /etc/systemd/coredump.conf or in a drop-in file under /etc/systemd/coredump.conf.d/ specifies the maximum size in bytes of a core which will be processed. Core dumps exceeding this size may be stored, but the backtrace will not be generated.
Rationale
A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers or system operators trying to debug problems. Enabling core dumps on production systems is not recommended, however there may be overriding operational requirements to enable advanced debuging. Permitting temporary enablement of core dumps during such situations should be reviewed through local needs and policy.
Warnings
warning  If the /etc/systemd/coredump.conf file or a drop-in file under /etc/systemd/coredump.conf.d/ does not already contain the [Coredump] section, the value will not be configured correctly.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q systemd; then

found=false

# set value in all files if they contain section or key
for f in $(echo -n "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[Coredump\]([^\n\[]*\n+)+?[[:space:]]*ProcessSizeMax" "$f"; then

            sed -i "s/ProcessSizeMax[^(\n)]*/ProcessSizeMax=0/" "$f"

            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[Coredump\]" "$f"; then

            sed -i "/[[:space:]]*\[Coredump\]/a ProcessSizeMax=0" "$f"

            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"

    echo -e "[Coredump]\nProcessSizeMax=0" >> "$file"

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82251-0
  - DISA-STIG-RHEL-08-010675
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_backtraces
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable core dump backtraces - Search for a section in files
  ansible.builtin.find:
    paths: '{{item.path}}'
    patterns: '{{item.pattern}}'
    contains: ^\s*\[Coredump\]
    read_whole_file: true
    use_regex: true
  register: systemd_dropin_files_with_section
  loop:
  - path: '{{ ''/etc/systemd/coredump.conf'' | dirname }}'
    pattern: '{{ ''/etc/systemd/coredump.conf'' | basename | regex_escape }}'
  - path: /etc/systemd/coredump.conf.d
    pattern: .*\.conf
  when: '"systemd" in ansible_facts.packages'
  tags:
  - CCE-82251-0
  - DISA-STIG-RHEL-08-010675
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_backtraces
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable core dump backtraces - Count number of files which contain the correct
    section
  ansible.builtin.set_fact:
    count_of_systemd_dropin_files_with_section: '{{systemd_dropin_files_with_section.results
      | map(attribute=''matched'') | list | map(''int'') | sum}}'
  when: '"systemd" in ansible_facts.packages'
  tags:
  - CCE-82251-0
  - DISA-STIG-RHEL-08-010675
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_backtraces
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable core dump backtraces - Add missing configuration to correct section
  community.general.ini_file:
    path: '{{item}}'
    section: Coredump
    option: ProcessSizeMax
    value: '0'
    state: present
    no_extra_spaces: true
  when:
  - '"systemd" in ansible_facts.packages'
  - count_of_systemd_dropin_files_with_section | int > 0
  loop: '{{systemd_dropin_files_with_section.results | sum(attribute=''files'', start=[])
    | map(attribute=''path'') | list }}'
  tags:
  - CCE-82251-0
  - DISA-STIG-RHEL-08-010675
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_backtraces
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable core dump backtraces - Add configuration to new remediation file
  community.general.ini_file:
    path: /etc/systemd/coredump.conf.d/complianceascode_hardening.conf
    section: Coredump
    option: ProcessSizeMax
    value: '0'
    state: present
    no_extra_spaces: true
    create: true
  when:
  - '"systemd" in ansible_facts.packages'
  - count_of_systemd_dropin_files_with_section | int == 0
  tags:
  - CCE-82251-0
  - DISA-STIG-RHEL-08-010675
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_backtraces
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A
        mode: 0644
        path: /etc/systemd/coredump.conf
        overwrite: true
OVAL test results details

tests the value of ProcessSizeMax setting in the /etc/systemd/coredump.conf file  oval:ssg-test_coredump_disable_backtraces:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_coredump_disable_backtraces:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/systemd/coredump.conf^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*ProcessSizeMax\h*=\h*(.+?)[ \t]*(?:$|#)1

tests the value of ProcessSizeMax setting in the /etc/systemd/coredump.conf.d file  oval:ssg-test_coredump_disable_backtraces_config_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_coredump_disable_backtraces_config_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/systemd/coredump.conf.d.*\.conf$^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*ProcessSizeMax\h*=\h*(.+?)[ \t]*(?:$|#)1
Disable storing core dumpxccdf_org.ssgproject.content_rule_coredump_disable_storage mediumCCE-82252-8

Disable storing core dump

Rule IDxccdf_org.ssgproject.content_rule_coredump_disable_storage
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-coredump_disable_storage:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-82252-8

References:
nistCM-6
pcidssReq-3.2
os-srgSRG-OS-000480-GPOS-00227
cis1.4.4
pcidss43.3.1.1, 3.3.1, 3.3
stigidRHEL-08-010674
stigrefSV-230314r1017125_rule
Description
The Storage option in [Coredump] section of /etc/systemd/coredump.conf or a drop-in file in /etc/systemd/coredump.conf.d/*.conf can be set to none to disable storing core dumps permanently.
Rationale
A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers or system operators trying to debug problems. Enabling core dumps on production systems is not recommended, however there may be overriding operational requirements to enable advanced debuging. Permitting temporary enablement of core dumps during such situations should be reviewed through local needs and policy.
Warnings
warning  If the /etc/systemd/coredump.conf file or a drop-in file under /etc/systemd/coredump.conf.d/ does not already contain the [Coredump] section, the value will not be configured correctly.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q systemd; then

found=false

# set value in all files if they contain section or key
for f in $(echo -n "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[Coredump\]([^\n\[]*\n+)+?[[:space:]]*Storage" "$f"; then

            sed -i "s/Storage[^(\n)]*/Storage=none/" "$f"

            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[Coredump\]" "$f"; then

            sed -i "/[[:space:]]*\[Coredump\]/a Storage=none" "$f"

            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"

    echo -e "[Coredump]\nStorage=none" >> "$file"

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82252-8
  - DISA-STIG-RHEL-08-010674
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_storage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable storing core dump - Search for a section in files
  ansible.builtin.find:
    paths: '{{item.path}}'
    patterns: '{{item.pattern}}'
    contains: ^\s*\[Coredump\]
    read_whole_file: true
    use_regex: true
  register: systemd_dropin_files_with_section
  loop:
  - path: '{{ ''/etc/systemd/coredump.conf'' | dirname }}'
    pattern: '{{ ''/etc/systemd/coredump.conf'' | basename | regex_escape }}'
  - path: /etc/systemd/coredump.conf.d
    pattern: .*\.conf
  when: '"systemd" in ansible_facts.packages'
  tags:
  - CCE-82252-8
  - DISA-STIG-RHEL-08-010674
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_storage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable storing core dump - Count number of files which contain the correct
    section
  ansible.builtin.set_fact:
    count_of_systemd_dropin_files_with_section: '{{systemd_dropin_files_with_section.results
      | map(attribute=''matched'') | list | map(''int'') | sum}}'
  when: '"systemd" in ansible_facts.packages'
  tags:
  - CCE-82252-8
  - DISA-STIG-RHEL-08-010674
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_storage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable storing core dump - Add missing configuration to correct section
  community.general.ini_file:
    path: '{{item}}'
    section: Coredump
    option: Storage
    value: none
    state: present
    no_extra_spaces: true
  when:
  - '"systemd" in ansible_facts.packages'
  - count_of_systemd_dropin_files_with_section | int > 0
  loop: '{{systemd_dropin_files_with_section.results | sum(attribute=''files'', start=[])
    | map(attribute=''path'') | list }}'
  tags:
  - CCE-82252-8
  - DISA-STIG-RHEL-08-010674
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_storage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable storing core dump - Add configuration to new remediation file
  community.general.ini_file:
    path: /etc/systemd/coredump.conf.d/complianceascode_hardening.conf
    section: Coredump
    option: Storage
    value: none
    state: present
    no_extra_spaces: true
    create: true
  when:
  - '"systemd" in ansible_facts.packages'
  - count_of_systemd_dropin_files_with_section | int == 0
  tags:
  - CCE-82252-8
  - DISA-STIG-RHEL-08-010674
  - NIST-800-53-CM-6
  - PCI-DSS-Req-3.2
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - coredump_disable_storage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,%23%20%20This%20file%20is%20part%20of%20systemd.%0A%23%0A%23%20%20systemd%20is%20free%20software%3B%20you%20can%20redistribute%20it%20and/or%20modify%20it%0A%23%20%20under%20the%20terms%20of%20the%20GNU%20Lesser%20General%20Public%20License%20as%20published%20by%0A%23%20%20the%20Free%20Software%20Foundation%3B%20either%20version%202.1%20of%20the%20License%2C%20or%0A%23%20%20%28at%20your%20option%29%20any%20later%20version.%0A%23%0A%23%20Entries%20in%20this%20file%20show%20the%20compile%20time%20defaults.%0A%23%20You%20can%20change%20settings%20by%20editing%20this%20file.%0A%23%20Defaults%20can%20be%20restored%20by%20simply%20deleting%20this%20file.%0A%23%0A%23%20See%20coredump.conf%285%29%20for%20details.%0A%0A%5BCoredump%5D%0A%23Storage%3Dexternal%0A%23Compress%3Dyes%0A%23ProcessSizeMax%3D2G%0A%23ExternalSizeMax%3D2G%0A%23JournalSizeMax%3D767M%0A%23MaxUse%3D%0A%23KeepFree%3D%0AStorage%3Dnone%0AProcessSizeMax%3D0%0A
        mode: 0644
        path: /etc/systemd/coredump.conf
        overwrite: true
OVAL test results details

tests the value of Storage setting in the /etc/systemd/coredump.conf file  oval:ssg-test_coredump_disable_storage:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_coredump_disable_storage:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/systemd/coredump.conf^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*Storage\h*=\h*(.+?)[ \t]*(?:$|#)1

tests the value of Storage setting in the /etc/systemd/coredump.conf.d file  oval:ssg-test_coredump_disable_storage_config_dir:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_coredump_disable_storage_config_dir:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/systemd/coredump.conf.d.*\.conf$^\s*\[Coredump\].*(?:\n\s*[^[\s].*)*\n^[ \t]*Storage\h*=\h*(.+?)[ \t]*(?:$|#)1
Disable Core Dumps for All Usersxccdf_org.ssgproject.content_rule_disable_users_coredumps mediumCCE-81038-2

Disable Core Dumps for All Users

Rule IDxccdf_org.ssgproject.content_rule_disable_users_coredumps
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-disable_users_coredumps:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-81038-2

References:
cis-csc1, 12, 13, 15, 16, 2, 7, 8
cobit5APO13.01, BAI04.04, DSS01.03, DSS03.05, DSS05.07
isa-62443-2013SR 6.2, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.17.2.1
nistCM-6, SC-7(10)
nist-csfDE.CM-1, PR.DS-4
os-srgSRG-OS-000480-GPOS-00227
pcidss43.3.1.1, 3.3.1, 3.3
stigidRHEL-08-010673
stigrefSV-230313r1069304_rule
Description
To disable core dumps for all users, add the following line to /etc/security/limits.conf, or to a file within the /etc/security/limits.d/ directory:
*     hard   core    0
Rationale
A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

SECURITY_LIMITS_FILE="/etc/security/limits.conf"
DROPIN_DIR="/etc/security/limits.d"
DROPIN_FILE="$DROPIN_DIR/10-ssg-hardening.conf"
REGEX_CORRECT_VALUE="^\s*\*\s+hard\s+core\s+0\s*$"

# Remove bad configuration in drop-ins
if [ -d "$DROPIN_DIR" ]; then
    for override in "$DROPIN_DIR"/*.conf; do
        if [ -f "$override" ] && ! grep -qE "$REGEX_CORRECT_VALUE" "$override"; then
            sed -ir -E '/^[[:space:]]*\*[[:space:]]+hard[[:space:]]+core[[:space:]]+/ s/^/#/' "$override"
        fi
    done
fi

if [ -d "$DROPIN_DIR" ] && grep -qEr "$REGEX_CORRECT_VALUE" "$DROPIN_DIR"; then
    exit 0
elif [ ! -d "$DROPIN_DIR" ] && grep -qE "$REGEX_CORRECT_VALUE" "$SECURITY_LIMITS_FILE"; then
    exit 0
else
    mkdir -p "$DROPIN_DIR"
    echo "*     hard   core    0" >> $DROPIN_FILE
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Set dirs, files and regex variables
  ansible.builtin.set_fact:
    limits_dropin_dir: /etc/security/limits.d
    limits_dropin_file: /etc/security/limits.d/10-ssg-hardening.conf
    limits_main_file: /etc/security/limits.conf
    limits_correct_regex: ^\s*\*\s+hard\s+core\s+0\s*$
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Find valid drop-ins for core limit
  ansible.builtin.find:
    paths: '{{ limits_dropin_dir }}'
    patterns: '*.conf'
    contains: '{{ limits_correct_regex }}'
    file_type: file
  register: valid_dropins
  failed_when: false
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Find all drop-ins with any core limit
  ansible.builtin.find:
    paths: '{{ limits_dropin_dir }}'
    patterns: '*.conf'
    contains: ^\s*\*\s+hard\s+core\s+
    file_type: file
  register: all_dropins
  failed_when: false
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Get invalid drop-ins
  ansible.builtin.set_fact:
    invalid_dropins: '{{ all_dropins.files | rejectattr(''path'', ''in'', valid_dropins.files
      | map(attribute=''path'') | list) | map(attribute=''path'') | list }}'
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Comment invalid * hard core lines in drop-ins
  ansible.builtin.replace:
    path: '{{ item }}'
    regexp: (^\s*\*\s+hard\s+core\s+.*$)
    replace: '#\1'
  loop: '{{ invalid_dropins }}'
  when:
  - '"pam" in ansible_facts.packages'
  - invalid_dropins | length > 0
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Check if main limits.conf contains correct
    core limit
  ansible.builtin.find:
    paths: /etc/security
    patterns: limits.conf
    contains: '{{ limits_correct_regex }}'
    file_type: file
  register: main_valid
  failed_when: false
  when:
  - '"pam" in ansible_facts.packages'
  - not (valid_dropins.matched | default(0) > 0)
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Set fact if configuration is valid
  ansible.builtin.set_fact:
    core_limit_valid: '{{ (valid_dropins.matched | default(0)) > 0 or (main_valid.matched
      | default(0)) > 0 }}'
  when: '"pam" in ansible_facts.packages'
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Ensure drop-in directory exists
  ansible.builtin.file:
    path: '{{ limits_dropin_dir }}'
    state: directory
  when:
  - '"pam" in ansible_facts.packages'
  - not core_limit_valid
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable Core Dumps for All Users - Deploy 10-ssg-hardening.conf drop-in with
    correct core limit
  ansible.builtin.copy:
    dest: '{{ limits_dropin_file }}'
    content: |
      *     hard   core    0
  when:
  - '"pam" in ansible_facts.packages'
  - not core_limit_valid
  tags:
  - CCE-81038-2
  - DISA-STIG-RHEL-08-010673
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_users_coredumps
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,%2A%20%20%20%20%20hard%20%20%20core%20%20%20%200
        mode: 0644
        path: /etc/security/limits.d/75-disable_users_coredumps.conf
        overwrite: true
OVAL test results details

Tests the value of the ^[\s]*\*[\s]+(hard|-)[\s]+core[\s]+([\d]+) setting in the /etc/security/limits.d directory  oval:ssg-test_core_dumps_limits_d:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_core_dumps_limits_d:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/security/limits.d^.*\.conf$^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\S]+)1

Tests for existance of the ^[\s]*\*[\s]+(hard|-)[\s]+core setting in the /etc/security/limits.d directory  oval:ssg-test_core_dumps_limits_d_exists:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_core_dumps_limits_d_exists:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/security/limits.d^.*\.conf$^[\s]*\*[\s]+(?:hard|-)[\s]+core1

Tests the value of the ^[\s]*\*[\s]+(hard|-)[\s]+core[\s]+([\d]+) setting in the /etc/security/limits.conf file  oval:ssg-test_core_dumps_limitsconf:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_core_dumps_limitsconf:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/security/limits.conf^[\s]*\*[\s]+(?:hard|-)[\s]+core[\s]+([\S]+)1
Restrict Exposed Kernel Pointer Addresses Accessxccdf_org.ssgproject.content_rule_sysctl_kernel_kptr_restrict mediumCCE-80915-2

Restrict Exposed Kernel Pointer Addresses Access

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_kptr_restrict
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_kptr_restrict:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-80915-2

References:
nerc-cipCIP-002-5 R1.1, CIP-002-5 R1.2, CIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 4.1, CIP-004-6 4.2, CIP-004-6 R2.2.3, CIP-004-6 R2.2.4, CIP-004-6 R2.3, CIP-004-6 R4, CIP-005-6 R1, CIP-005-6 R1.1, CIP-005-6 R1.2, CIP-007-3 R3, CIP-007-3 R3.1, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3, CIP-007-3 R8.4, CIP-009-6 R.1.1, CIP-009-6 R4
nistSC-30, SC-30(2), SC-30(5), CM-6(a)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000132-GPOS-00067, SRG-OS-000433-GPOS-00192, SRG-OS-000480-GPOS-00227
anssiR9
stigidRHEL-08-040283
stigrefSV-230547r1017309_rule
Description
To set the runtime status of the kernel.kptr_restrict kernel parameter, run the following command:
$ sudo sysctl -w kernel.kptr_restrict=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.kptr_restrict = 1
Rationale
Exposing kernel pointers (through procfs or seq_printf()) exposes kernel writeable structures which may contain functions pointers. If a write vulnerability occurs in the kernel, allowing write access to any of this structure, the kernel can be compromised. This option disallow any program without the CAP_SYSLOG capability to get the addresses of kernel pointers by replacing them with 0.
OVAL test results details

kernel.kptr_restrict static configuration  oval:ssg-test_sysctl_kernel_kptr_restrict_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_kptr_restrict:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_kptr_restrict:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_kptr_restrict:obj:1

kernel.kptr_restrict static configuration  oval:ssg-test_sysctl_kernel_kptr_restrict_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_kptr_restrict:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_kptr_restrict:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_kptr_restrict:obj:1

kernel.kptr_restrict static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_kptr_restrict_static_pkg_correct:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/usr/lib/sysctl.d/50-default.confkernel.kptr_restrict = 1

kernel runtime parameter kernel.kptr_restrict set to 1 or 2  oval:ssg-test_sysctl_kernel_kptr_restrict_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truekernel.kptr_restrict1
Enable Randomized Layout of Virtual Address Spacexccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space mediumCCE-80916-0

Enable Randomized Layout of Virtual Address Space

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_randomize_va_space:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-80916-0

References:
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e)
nerc-cipCIP-002-5 R1.1, CIP-002-5 R1.2, CIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 4.1, CIP-004-6 4.2, CIP-004-6 R2.2.3, CIP-004-6 R2.2.4, CIP-004-6 R2.3, CIP-004-6 R4, CIP-005-6 R1, CIP-005-6 R1.1, CIP-005-6 R1.2, CIP-007-3 R3, CIP-007-3 R3.1, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3, CIP-007-3 R8.4, CIP-009-6 R.1.1, CIP-009-6 R4
nistSC-30, SC-30(2), CM-6(a)
pcidssReq-2.2.1
os-srgSRG-OS-000433-GPOS-00193, SRG-OS-000480-GPOS-00227
app-srg-ctrSRG-APP-000450-CTR-001105
anssiR9
cis1.4.1
pcidss43.3.1.1, 3.3.1, 3.3
stigidRHEL-08-010430
stigrefSV-230280r1017093_rule
Description
To set the runtime status of the kernel.randomize_va_space kernel parameter, run the following command:
$ sudo sysctl -w kernel.randomize_va_space=2
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.randomize_va_space = 2
Rationale
Address space layout randomization (ASLR) makes it more difficult for an attacker to predict the location of attack code they have introduced into a process's address space during an attempt at exploitation. Additionally, ASLR makes it more difficult for an attacker to know the location of existing code in order to re-purpose it using return oriented programming (ROP) techniques.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.randomize_va_space.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.randomize_va_space" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.randomize_va_space
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.randomize_va_space="2"
fi

#
# If kernel.randomize_va_space present in /etc/sysctl.conf, change value to "2"
#	else, add "kernel.randomize_va_space = 2" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.randomize_va_space")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "2"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.randomize_va_space\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.randomize_va_space\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80916-0"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80916-0
  - DISA-STIG-RHEL-08-010430
  - NIST-800-171-3.1.7
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-30
  - NIST-800-53-SC-30(2)
  - PCI-DSS-Req-2.2.1
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_randomize_va_space

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.randomize_va_space.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80916-0
  - DISA-STIG-RHEL-08-010430
  - NIST-800-171-3.1.7
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-30
  - NIST-800-53-SC-30(2)
  - PCI-DSS-Req-2.2.1
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_randomize_va_space

- name: Comment out any occurrences of kernel.randomize_va_space from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.randomize_va_space
    replace: '#kernel.randomize_va_space'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80916-0
  - DISA-STIG-RHEL-08-010430
  - NIST-800-171-3.1.7
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-30
  - NIST-800-53-SC-30(2)
  - PCI-DSS-Req-2.2.1
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_randomize_va_space

- name: Ensure sysctl kernel.randomize_va_space is set to 2
  ansible.posix.sysctl:
    name: kernel.randomize_va_space
    value: '2'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80916-0
  - DISA-STIG-RHEL-08-010430
  - NIST-800-171-3.1.7
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-30
  - NIST-800-53-SC-30(2)
  - PCI-DSS-Req-2.2.1
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_randomize_va_space

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.randomize_va_space%3D2%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_randomize_va_space.conf
        overwrite: true
OVAL test results details

kernel.randomize_va_space static configuration  oval:ssg-test_sysctl_kernel_randomize_va_space_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_randomize_va_space:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_randomize_va_space:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_randomize_va_space:obj:1

kernel.randomize_va_space static configuration  oval:ssg-test_sysctl_kernel_randomize_va_space_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_randomize_va_space:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_randomize_va_space:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_randomize_va_space:obj:1

kernel.randomize_va_space static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_randomize_va_space_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_kernel_randomize_va_space:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*kernel.randomize_va_space[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter kernel.randomize_va_space set to 2  oval:ssg-test_sysctl_kernel_randomize_va_space_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truekernel.randomize_va_space2
Enable NX or XD Support in the BIOSxccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions mediumCCE-83918-3

Enable NX or XD Support in the BIOS

Rule IDxccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-bios_enable_execution_restrictions:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-83918-3

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.7
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistSC-39, CM-6(a)
nist-csfPR.IP-1
os-srgSRG-OS-000433-GPOS-00192
app-srg-ctrSRG-APP-000450-CTR-001105
pcidss42.2.1, 2.2
stigidRHEL-08-010420
stigrefSV-230276r958928_rule
Description
Reboot the system and enter the BIOS or Setup configuration menu. Navigate the BIOS configuration menu and make sure that the option is enabled. The setting may be located under a Security section. Look for Execute Disable (XD) on Intel-based systems and No Execute (NX) on AMD-based systems.
Rationale
Computers with the ability to prevent this type of code execution frequently put an option in the BIOS that will allow users to turn the feature on or off at will.
OVAL test results details

CPUs support for NX bit  oval:ssg-test_NX_cpu_support:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/proc/cpuinfoflags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm constant_tsc nopl xtopology cpuid tsc_known_freq pni ssse3 cx16 sse4_1 sse4_2 x2apic popcnt hypervisor lahf_lm cpuid_fault pti

NX is not disabled in the kernel command line  oval:ssg-test_noexec_cmd_line:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_noexec_cmd_line:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/proc/cmdline.+noexec[0-9]*=off.+1
Enable page allocator poisoningxccdf_org.ssgproject.content_rule_grub2_page_poison_argument mediumCCE-80944-2

Enable page allocator poisoning

Rule IDxccdf_org.ssgproject.content_rule_grub2_page_poison_argument
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_page_poison_argument:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-80944-2

References:
nistCM-6(a)
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000134-GPOS-00068
anssiR8
stigidRHEL-08-010421
stigrefSV-230277r1017090_rule
Description
To enable poisoning of free pages, add the argument page_poison=1 to the default GRUB 2 command line for the Linux operating system. To ensure that page_poison=1 is added as a kernel command line argument to newly installed kernels, add page_poison=1 to the default Grub2 command line for Linux operating systems. Modify the line within /etc/default/grub as shown below:
GRUB_CMDLINE_LINUX="... page_poison=1 ..."
Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --args="page_poison=1"
Rationale
Poisoning writes an arbitrary value to freed pages, so any modification or reference to that page after being freed or before being initialized will be detected and prevented. This prevents many types of use-after-free vulnerabilities at little performance cost. Also prevents leak of data and detection of corrupted memory.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q grub2-common; }; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "page_poison" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"page_poison=[^\"]*\"(.*]\s*)/\1\"page_poison=1\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"page_poison=1\"]" >> "$KARGS_DIR/10-page_poison.toml"
    fi
else

    grubby --update-kernel=ALL --args=page_poison=1 --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80944-2
  - DISA-STIG-RHEL-08-010421
  - NIST-800-53-CM-6(a)
  - grub2_page_poison_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="page_poison=1"
  when:
  - '"kernel" in ansible_facts.packages'
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - CCE-80944-2
  - DISA-STIG-RHEL-08-010421
  - NIST-800-53-CM-6(a)
  - grub2_page_poison_argument
  - low_disruption
  - medium_complexity
  - medium_severity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "page_poison=1"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader page_poison=1
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of page_poison=1. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_page_poison_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_page_poison_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters page_poison=1 in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_page_poison_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters page_poison=1 in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_page_poison_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_page_poison_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for page_poison=1 in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_page_poison_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for page_poison=1 in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_page_poison_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_page_poison_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Disable the uvcvideo modulexccdf_org.ssgproject.content_rule_kernel_module_uvcvideo_disabled mediumCCE-86960-2

Disable the uvcvideo module

Rule IDxccdf_org.ssgproject.content_rule_kernel_module_uvcvideo_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-kernel_module_uvcvideo_disabled:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-86960-2

References:
nistCM-7 (a), CM-7 (5) (b)
os-srgSRG-OS-000095-GPOS-00049, SRG-OS-000370-GPOS-00155
stigidRHEL-08-040020
stigrefSV-230493r1017276_rule
Description
If the device contains a camera it should be covered or disabled when not in use.
Rationale
Failing to disconnect from collaborative computing devices (i.e., cameras) can result in subsequent compromises of organizational information. Providing easy methods to physically disconnect from such devices after a collaborative computing session helps to ensure participants actually carry out the disconnect activity without having to go through complex and tedious procedures.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if LC_ALL=C grep -q -m 1 "^install uvcvideo" /etc/modprobe.d/uvcvideo.conf ; then
	
	sed -i 's#^install uvcvideo.*#install uvcvideo /bin/false#g' /etc/modprobe.d/uvcvideo.conf
else
	echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/uvcvideo.conf
	echo "install uvcvideo /bin/false" >> /etc/modprobe.d/uvcvideo.conf
fi

if ! LC_ALL=C grep -q -m 1 "^blacklist uvcvideo$" /etc/modprobe.d/uvcvideo.conf ; then
	echo "blacklist uvcvideo" >> /etc/modprobe.d/uvcvideo.conf
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86960-2
  - DISA-STIG-RHEL-08-040020
  - NIST-800-53-CM-7 (5) (b)
  - NIST-800-53-CM-7 (a)
  - disable_strategy
  - kernel_module_uvcvideo_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'uvcvideo' is disabled
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/uvcvideo.conf
    regexp: install\s+uvcvideo
    line: install uvcvideo /bin/false
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86960-2
  - DISA-STIG-RHEL-08-040020
  - NIST-800-53-CM-7 (5) (b)
  - NIST-800-53-CM-7 (a)
  - disable_strategy
  - kernel_module_uvcvideo_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

- name: Ensure kernel module 'uvcvideo' is blacklisted
  ansible.builtin.lineinfile:
    create: true
    dest: /etc/modprobe.d/uvcvideo.conf
    regexp: ^blacklist uvcvideo$
    line: blacklist uvcvideo
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86960-2
  - DISA-STIG-RHEL-08-040020
  - NIST-800-53-CM-7 (5) (b)
  - NIST-800-53-CM-7 (a)
  - disable_strategy
  - kernel_module_uvcvideo_disabled
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,install%20uvcvideo%20/bin/false%0Ablacklist%20uvcvideo%0A
        mode: 0644
        path: /etc/modprobe.d/uvcvideo.conf
        overwrite: true
OVAL test results details

kernel module uvcvideo blacklisted  oval:ssg-test_kernmod_uvcvideo_blacklisted:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_uvcvideo_blacklisted:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^blacklist\s+uvcvideo$1

kernel module uvcvideo disabled  oval:ssg-test_kernmod_uvcvideo_disabled:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_kernmod_uvcvideo_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/etc/modprobe.d
/etc/modules-load.d
/run/modprobe.d
/run/modules-load.d
/usr/lib/modprobe.d
/usr/lib/modules-load.d
^.*\.conf$^\s*install\s+uvcvideo\s+(/bin/false|/bin/true)$1
Disable storing core dumpsxccdf_org.ssgproject.content_rule_sysctl_kernel_core_pattern mediumCCE-82215-5

Disable storing core dumps

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_core_pattern
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_core_pattern:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82215-5

References:
nistSC-7(10)
os-srgSRG-OS-000480-GPOS-00227
pcidss43.3.1.1, 3.3.1, 3.3
stigidRHEL-08-010671
stigrefSV-230311r1017121_rule
Description
To set the runtime status of the kernel.core_pattern kernel parameter, run the following command:
$ sudo sysctl -w kernel.core_pattern=|/bin/false
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.core_pattern = |/bin/false
Rationale
A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_pattern.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.core_pattern" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.core_pattern
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.core_pattern="|/bin/false"
fi

#
# If kernel.core_pattern present in /etc/sysctl.conf, change value to "|/bin/false"
#	else, add "kernel.core_pattern = |/bin/false" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_pattern")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "|/bin/false"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_pattern\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.core_pattern\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-82215-5"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82215-5
  - DISA-STIG-RHEL-08-010671
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_core_pattern

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.core_pattern.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82215-5
  - DISA-STIG-RHEL-08-010671
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_core_pattern

- name: Comment out any occurrences of kernel.core_pattern from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.core_pattern
    replace: '#kernel.core_pattern'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82215-5
  - DISA-STIG-RHEL-08-010671
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_core_pattern

- name: Ensure sysctl kernel.core_pattern is set to |/bin/false
  ansible.posix.sysctl:
    name: kernel.core_pattern
    value: '|/bin/false'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82215-5
  - DISA-STIG-RHEL-08-010671
  - NIST-800-53-SC-7(10)
  - PCI-DSSv4-3.3
  - PCI-DSSv4-3.3.1
  - PCI-DSSv4-3.3.1.1
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_core_pattern

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.core_pattern%20%3D%20%7C/bin/false%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_core_pattern.conf
        overwrite: true
OVAL test results details

kernel.core_pattern static configuration  oval:ssg-test_sysctl_kernel_core_pattern_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_core_pattern:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_core_pattern:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_core_pattern:obj:1

kernel.core_pattern static configuration  oval:ssg-test_sysctl_kernel_core_pattern_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_core_pattern:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_core_pattern:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_core_pattern:obj:1

kernel.core_pattern static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_core_pattern_static_pkg_correct:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/usr/lib/sysctl.d/50-coredump.conf kernel.core_pattern=|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e

kernel runtime parameter kernel.core_pattern set to |/bin/false  oval:ssg-test_sysctl_kernel_core_pattern_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsekernel.core_pattern|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e
Restrict Access to Kernel Message Bufferxccdf_org.ssgproject.content_rule_sysctl_kernel_dmesg_restrict lowCCE-80913-7

Restrict Access to Kernel Message Buffer

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_dmesg_restrict
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_dmesg_restrict:def:1
Time2025-11-18T17:07:01-05:00
Severitylow
Identifiers:

CCE-80913-7

References:
cui3.1.5
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e)
nistSI-11(a), SI-11(b)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000132-GPOS-00067, SRG-OS-000138-GPOS-00069
app-srg-ctrSRG-APP-000243-CTR-000600
anssiR9
stigidRHEL-08-010375
stigrefSV-230269r1017087_rule
Description
To set the runtime status of the kernel.dmesg_restrict kernel parameter, run the following command:
$ sudo sysctl -w kernel.dmesg_restrict=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.dmesg_restrict = 1
Rationale
Unprivileged access to the kernel syslog can expose sensitive kernel address information.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.dmesg_restrict.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.dmesg_restrict" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.dmesg_restrict
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.dmesg_restrict="1"
fi

#
# If kernel.dmesg_restrict present in /etc/sysctl.conf, change value to "1"
#	else, add "kernel.dmesg_restrict = 1" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.dmesg_restrict")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.dmesg_restrict\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.dmesg_restrict\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80913-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80913-7
  - DISA-STIG-RHEL-08-010375
  - NIST-800-171-3.1.5
  - NIST-800-53-SI-11(a)
  - NIST-800-53-SI-11(b)
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_dmesg_restrict

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.dmesg_restrict.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80913-7
  - DISA-STIG-RHEL-08-010375
  - NIST-800-171-3.1.5
  - NIST-800-53-SI-11(a)
  - NIST-800-53-SI-11(b)
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_dmesg_restrict

- name: Comment out any occurrences of kernel.dmesg_restrict from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.dmesg_restrict
    replace: '#kernel.dmesg_restrict'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80913-7
  - DISA-STIG-RHEL-08-010375
  - NIST-800-171-3.1.5
  - NIST-800-53-SI-11(a)
  - NIST-800-53-SI-11(b)
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_dmesg_restrict

- name: Ensure sysctl kernel.dmesg_restrict is set to 1
  ansible.posix.sysctl:
    name: kernel.dmesg_restrict
    value: '1'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80913-7
  - DISA-STIG-RHEL-08-010375
  - NIST-800-171-3.1.5
  - NIST-800-53-SI-11(a)
  - NIST-800-53-SI-11(b)
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_dmesg_restrict

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.dmesg_restrict%3D1%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_dmesg_restrict.conf
        overwrite: true
OVAL test results details

kernel.dmesg_restrict static configuration  oval:ssg-test_sysctl_kernel_dmesg_restrict_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_dmesg_restrict:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_dmesg_restrict:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_dmesg_restrict:obj:1

kernel.dmesg_restrict static configuration  oval:ssg-test_sysctl_kernel_dmesg_restrict_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_dmesg_restrict:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_dmesg_restrict:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_dmesg_restrict:obj:1

kernel.dmesg_restrict static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_dmesg_restrict_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_kernel_dmesg_restrict:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*kernel.dmesg_restrict[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter kernel.dmesg_restrict set to 1  oval:ssg-test_sysctl_kernel_dmesg_restrict_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsekernel.dmesg_restrict0
Disable Kernel Image Loadingxccdf_org.ssgproject.content_rule_sysctl_kernel_kexec_load_disabled mediumCCE-80952-5

Disable Kernel Image Loading

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_kexec_load_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_kexec_load_disabled:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80952-5

References:
nistCM-6
osppFMT_SMF_EXT.1
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000366-GPOS-00153
stigidRHEL-08-010372
stigrefSV-230266r1017084_rule
Description
To set the runtime status of the kernel.kexec_load_disabled kernel parameter, run the following command:
$ sudo sysctl -w kernel.kexec_load_disabled=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.kexec_load_disabled = 1
Rationale
Disabling kexec_load allows greater control of the kernel memory. It makes it impossible to load another kernel image after it has been disabled.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.kexec_load_disabled.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.kexec_load_disabled" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.kexec_load_disabled
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.kexec_load_disabled="1"
fi

#
# If kernel.kexec_load_disabled present in /etc/sysctl.conf, change value to "1"
#	else, add "kernel.kexec_load_disabled = 1" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.kexec_load_disabled")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.kexec_load_disabled\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.kexec_load_disabled\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80952-5"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80952-5
  - DISA-STIG-RHEL-08-010372
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_kexec_load_disabled

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.kexec_load_disabled.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80952-5
  - DISA-STIG-RHEL-08-010372
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_kexec_load_disabled

- name: Comment out any occurrences of kernel.kexec_load_disabled from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.kexec_load_disabled
    replace: '#kernel.kexec_load_disabled'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80952-5
  - DISA-STIG-RHEL-08-010372
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_kexec_load_disabled

- name: Ensure sysctl kernel.kexec_load_disabled is set to 1
  ansible.posix.sysctl:
    name: kernel.kexec_load_disabled
    value: '1'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80952-5
  - DISA-STIG-RHEL-08-010372
  - NIST-800-53-CM-6
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_kexec_load_disabled

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.kexec_load_disabled%3D1%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_kexec_load_disabled.conf
        overwrite: true
OVAL test results details

kernel.kexec_load_disabled static configuration  oval:ssg-test_sysctl_kernel_kexec_load_disabled_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_kexec_load_disabled:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_kexec_load_disabled:obj:1

kernel.kexec_load_disabled static configuration  oval:ssg-test_sysctl_kernel_kexec_load_disabled_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_kexec_load_disabled:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_kexec_load_disabled:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_kexec_load_disabled:obj:1

kernel.kexec_load_disabled static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_kexec_load_disabled_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_kernel_kexec_load_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*kernel.kexec_load_disabled[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter kernel.kexec_load_disabled set to 1  oval:ssg-test_sysctl_kernel_kexec_load_disabled_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsekernel.kexec_load_disabled0
Disallow kernel profiling by unprivileged usersxccdf_org.ssgproject.content_rule_sysctl_kernel_perf_event_paranoid lowCCE-81054-9

Disallow kernel profiling by unprivileged users

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_perf_event_paranoid
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_perf_event_paranoid:def:1
Time2025-11-18T17:07:01-05:00
Severitylow
Identifiers:

CCE-81054-9

References:
nistAC-6
osppFMT_SMF_EXT.1
os-srgSRG-OS-000132-GPOS-00067, SRG-OS-000138-GPOS-00069
app-srg-ctrSRG-APP-000243-CTR-000600
anssiR9
stigidRHEL-08-010376
stigrefSV-230270r1017088_rule
Description
To set the runtime status of the kernel.perf_event_paranoid kernel parameter, run the following command:
$ sudo sysctl -w kernel.perf_event_paranoid=2
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.perf_event_paranoid = 2
Rationale
Kernel profiling can reveal sensitive information about kernel behaviour.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.perf_event_paranoid from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.perf_event_paranoid.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.perf_event_paranoid" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.perf_event_paranoid
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.perf_event_paranoid="2"
fi

#
# If kernel.perf_event_paranoid present in /etc/sysctl.conf, change value to "2"
#	else, add "kernel.perf_event_paranoid = 2" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.perf_event_paranoid")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "2"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.perf_event_paranoid\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.perf_event_paranoid\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-81054-9"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-81054-9
  - DISA-STIG-RHEL-08-010376
  - NIST-800-53-AC-6
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_perf_event_paranoid

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.perf_event_paranoid.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81054-9
  - DISA-STIG-RHEL-08-010376
  - NIST-800-53-AC-6
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_perf_event_paranoid

- name: Comment out any occurrences of kernel.perf_event_paranoid from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.perf_event_paranoid
    replace: '#kernel.perf_event_paranoid'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81054-9
  - DISA-STIG-RHEL-08-010376
  - NIST-800-53-AC-6
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_perf_event_paranoid

- name: Ensure sysctl kernel.perf_event_paranoid is set to 2
  ansible.posix.sysctl:
    name: kernel.perf_event_paranoid
    value: '2'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-81054-9
  - DISA-STIG-RHEL-08-010376
  - NIST-800-53-AC-6
  - disable_strategy
  - low_complexity
  - low_severity
  - medium_disruption
  - reboot_required
  - sysctl_kernel_perf_event_paranoid

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.perf_event_paranoid%3D2%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_perf_event_paranoid.conf
        overwrite: true
OVAL test results details

kernel.perf_event_paranoid static configuration  oval:ssg-test_sysctl_kernel_perf_event_paranoid_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_perf_event_paranoid:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_perf_event_paranoid:obj:1

kernel.perf_event_paranoid static configuration  oval:ssg-test_sysctl_kernel_perf_event_paranoid_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_perf_event_paranoid:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_perf_event_paranoid:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_perf_event_paranoid:obj:1

kernel.perf_event_paranoid static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_perf_event_paranoid_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_kernel_perf_event_paranoid:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*kernel.perf_event_paranoid[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter kernel.perf_event_paranoid set to 2  oval:ssg-test_sysctl_kernel_perf_event_paranoid_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truekernel.perf_event_paranoid2
Disable Access to Network bpf() Syscall From Unprivileged Processesxccdf_org.ssgproject.content_rule_sysctl_kernel_unprivileged_bpf_disabled mediumCCE-82974-7

Disable Access to Network bpf() Syscall From Unprivileged Processes

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_unprivileged_bpf_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_unprivileged_bpf_disabled:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82974-7

References:
nistAC-6, SC-7(10)
os-srgSRG-OS-000132-GPOS-00067, SRG-OS-000480-GPOS-00227
anssiR9
stigidRHEL-08-040281
stigrefSV-230545r1017307_rule
Description
To set the runtime status of the kernel.unprivileged_bpf_disabled kernel parameter, run the following command:
$ sudo sysctl -w kernel.unprivileged_bpf_disabled=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.unprivileged_bpf_disabled = 1
Rationale
Loading and accessing the packet filters programs and maps using the bpf() syscall has the potential of revealing sensitive information about the kernel state.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.unprivileged_bpf_disabled.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.unprivileged_bpf_disabled" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.unprivileged_bpf_disabled
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.unprivileged_bpf_disabled="1"
fi

#
# If kernel.unprivileged_bpf_disabled present in /etc/sysctl.conf, change value to "1"
#	else, add "kernel.unprivileged_bpf_disabled = 1" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.unprivileged_bpf_disabled")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.unprivileged_bpf_disabled\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.unprivileged_bpf_disabled\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-82974-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82974-7
  - DISA-STIG-RHEL-08-040281
  - NIST-800-53-AC-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_unprivileged_bpf_disabled

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.unprivileged_bpf_disabled.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82974-7
  - DISA-STIG-RHEL-08-040281
  - NIST-800-53-AC-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_unprivileged_bpf_disabled

- name: Comment out any occurrences of kernel.unprivileged_bpf_disabled from config
    files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.unprivileged_bpf_disabled
    replace: '#kernel.unprivileged_bpf_disabled'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82974-7
  - DISA-STIG-RHEL-08-040281
  - NIST-800-53-AC-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_unprivileged_bpf_disabled

- name: Ensure sysctl kernel.unprivileged_bpf_disabled is set to 1
  ansible.posix.sysctl:
    name: kernel.unprivileged_bpf_disabled
    value: '1'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82974-7
  - DISA-STIG-RHEL-08-040281
  - NIST-800-53-AC-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_unprivileged_bpf_disabled

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.unprivileged_bpf_disabled%3D1%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_unprivileged_bpf_disabled.conf
        overwrite: true
OVAL test results details

kernel.unprivileged_bpf_disabled static configuration  oval:ssg-test_sysctl_kernel_unprivileged_bpf_disabled_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_unprivileged_bpf_disabled:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1

kernel.unprivileged_bpf_disabled static configuration  oval:ssg-test_sysctl_kernel_unprivileged_bpf_disabled_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_unprivileged_bpf_disabled:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_unprivileged_bpf_disabled:obj:1

kernel.unprivileged_bpf_disabled static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_unprivileged_bpf_disabled_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_kernel_unprivileged_bpf_disabled:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*kernel.unprivileged_bpf_disabled[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter kernel.unprivileged_bpf_disabled set to 1  oval:ssg-test_sysctl_kernel_unprivileged_bpf_disabled_runtime:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameValue
truekernel.unprivileged_bpf_disabled1
Restrict usage of ptrace to descendant processesxccdf_org.ssgproject.content_rule_sysctl_kernel_yama_ptrace_scope mediumCCE-80953-3

Restrict usage of ptrace to descendant processes

Rule IDxccdf_org.ssgproject.content_rule_sysctl_kernel_yama_ptrace_scope
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_kernel_yama_ptrace_scope:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-80953-3

References:
nistSC-7(10)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000132-GPOS-00067, SRG-OS-000480-GPOS-00227
anssiR11
cis1.4.2
stigidRHEL-08-040282
stigrefSV-230546r1017308_rule
Description
To set the runtime status of the kernel.yama.ptrace_scope kernel parameter, run the following command:
$ sudo sysctl -w kernel.yama.ptrace_scope=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
kernel.yama.ptrace_scope = 1
Rationale
Unrestricted usage of ptrace allows compromised binaries to run ptrace on another processes of the user. Like this, the attacker can steal sensitive information from the target processes (e.g. SSH sessions, web browser, ...) without any additional assistance from the user (i.e. without resorting to phishing).

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*kernel.yama.ptrace_scope.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "kernel.yama.ptrace_scope" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for kernel.yama.ptrace_scope
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w kernel.yama.ptrace_scope="1"
fi

#
# If kernel.yama.ptrace_scope present in /etc/sysctl.conf, change value to "1"
#	else, add "kernel.yama.ptrace_scope = 1" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.yama.ptrace_scope")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "1"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^kernel.yama.ptrace_scope\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^kernel.yama.ptrace_scope\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-80953-3"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80953-3
  - DISA-STIG-RHEL-08-040282
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_yama_ptrace_scope

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*kernel.yama.ptrace_scope.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80953-3
  - DISA-STIG-RHEL-08-040282
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_yama_ptrace_scope

- name: Comment out any occurrences of kernel.yama.ptrace_scope from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*kernel.yama.ptrace_scope
    replace: '#kernel.yama.ptrace_scope'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80953-3
  - DISA-STIG-RHEL-08-040282
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_yama_ptrace_scope

- name: Ensure sysctl kernel.yama.ptrace_scope is set to 1
  ansible.posix.sysctl:
    name: kernel.yama.ptrace_scope
    value: '1'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80953-3
  - DISA-STIG-RHEL-08-040282
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_kernel_yama_ptrace_scope

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,kernel.yama.ptrace_scope%3D1%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_kernel_yama_ptrace_scope.conf
        overwrite: true
OVAL test results details

kernel.yama.ptrace_scope static configuration  oval:ssg-test_sysctl_kernel_yama_ptrace_scope_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_yama_ptrace_scope:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1

kernel.yama.ptrace_scope static configuration  oval:ssg-test_sysctl_kernel_yama_ptrace_scope_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_kernel_yama_ptrace_scope:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_kernel_yama_ptrace_scope:obj:1

kernel.yama.ptrace_scope static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_kernel_yama_ptrace_scope_static_pkg_correct:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/usr/lib/sysctl.d/10-default-yama-scope.confkernel.yama.ptrace_scope = 0

kernel runtime parameter kernel.yama.ptrace_scope set to 1  oval:ssg-test_sysctl_kernel_yama_ptrace_scope_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsekernel.yama.ptrace_scope0
Harden the operation of the BPF just-in-time compilerxccdf_org.ssgproject.content_rule_sysctl_net_core_bpf_jit_harden mediumCCE-82934-1

Harden the operation of the BPF just-in-time compiler

Rule IDxccdf_org.ssgproject.content_rule_sysctl_net_core_bpf_jit_harden
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_net_core_bpf_jit_harden:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-82934-1

References:
nistCM-6, SC-7(10)
os-srgSRG-OS-000480-GPOS-00227
anssiR12
stigidRHEL-08-040286
stigrefSV-244554r1017354_rule
Description
To set the runtime status of the net.core.bpf_jit_harden kernel parameter, run the following command:
$ sudo sysctl -w net.core.bpf_jit_harden=2
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
net.core.bpf_jit_harden = 2
Rationale
When hardened, the extended Berkeley Packet Filter just-in-time compiler will randomize any kernel addresses in the BPF programs and maps, and will not expose the JIT addresses in /proc/kallsyms.

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

# Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf files

for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do


  # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf)
  if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi

  matching_list=$(grep -P '^(?!#).*[\s]*net.core.bpf_jit_harden.*$' $f | uniq )
  if ! test -z "$matching_list"; then
    while IFS= read -r entry; do
      escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry")
      # comment out "net.core.bpf_jit_harden" matches to preserve user data
      sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f
    done <<< "$matching_list"
  fi
done

#
# Set sysctl config file which to save the desired value
#

SYSCONFIG_FILE="/etc/sysctl.conf"


#
# Set runtime for net.core.bpf_jit_harden
#
if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    /sbin/sysctl -q -n -w net.core.bpf_jit_harden="2"
fi

#
# If net.core.bpf_jit_harden present in /etc/sysctl.conf, change value to "2"
#	else, add "net.core.bpf_jit_harden = 2" to /etc/sysctl.conf
#

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.core.bpf_jit_harden")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "2"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^net.core.bpf_jit_harden\\>" "${SYSCONFIG_FILE}"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^net.core.bpf_jit_harden\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}"
else
    if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}"
    fi
    cce="CCE-82934-1"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}"
    printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82934-1
  - DISA-STIG-RHEL-08-040286
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_core_bpf_jit_harden

- name: List /etc/sysctl.d/*.conf files
  ansible.builtin.find:
    paths:
    - /etc/sysctl.d/
    - /run/sysctl.d/
    - /usr/local/lib/sysctl.d/
    contains: ^[\s]*net.core.bpf_jit_harden.*$
    patterns: '*.conf'
    file_type: any
  register: find_sysctl_d
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82934-1
  - DISA-STIG-RHEL-08-040286
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_core_bpf_jit_harden

- name: Comment out any occurrences of net.core.bpf_jit_harden from config files
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^[\s]*net.core.bpf_jit_harden
    replace: '#net.core.bpf_jit_harden'
  loop: '{{ find_sysctl_d.files }}'
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82934-1
  - DISA-STIG-RHEL-08-040286
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_core_bpf_jit_harden

- name: Ensure sysctl net.core.bpf_jit_harden is set to 2
  ansible.posix.sysctl:
    name: net.core.bpf_jit_harden
    value: '2'
    sysctl_file: /etc/sysctl.conf
    state: present
    reload: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82934-1
  - DISA-STIG-RHEL-08-040286
  - NIST-800-53-CM-6
  - NIST-800-53-SC-7(10)
  - disable_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - reboot_required
  - sysctl_net_core_bpf_jit_harden

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,net.core.bpf_jit_harden%3D2%0A
        mode: 0644
        path: /etc/sysctl.d/75-sysctl_net_core_bpf_jit_harden.conf
        overwrite: true
OVAL test results details

net.core.bpf_jit_harden static configuration  oval:ssg-test_sysctl_net_core_bpf_jit_harden_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_core_bpf_jit_harden:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_core_bpf_jit_harden:obj:1

net.core.bpf_jit_harden static configuration  oval:ssg-test_sysctl_net_core_bpf_jit_harden_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_net_core_bpf_jit_harden:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_net_core_bpf_jit_harden:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_net_core_bpf_jit_harden:obj:1

net.core.bpf_jit_harden static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_net_core_bpf_jit_harden_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_net_core_bpf_jit_harden:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*net.core.bpf_jit_harden[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter net.core.bpf_jit_harden set to 2  oval:ssg-test_sysctl_net_core_bpf_jit_harden_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falsenet.core.bpf_jit_harden1
Disable the use of user namespacesxccdf_org.ssgproject.content_rule_sysctl_user_max_user_namespaces_no_remediation mediumCCE-86207-8

Disable the use of user namespaces

Rule IDxccdf_org.ssgproject.content_rule_sysctl_user_max_user_namespaces_no_remediation
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sysctl_user_max_user_namespaces_no_remediation:def:1
Time2025-11-18T17:07:01-05:00
Severitymedium
Identifiers:

CCE-86207-8

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040284
stigrefSV-230548r1017310_rule
Description
To set the runtime status of the user.max_user_namespaces kernel parameter, run the following command:
$ sudo sysctl -w user.max_user_namespaces=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d:
user.max_user_namespaces = 0
When containers are deployed on the machine, the value should be set to large non-zero value.
Rationale
It is detrimental for operating systems to provide, or install by default, functionality exceeding requirements or system objectives. These unnecessary capabilities or services are often overlooked and therefore may remain unsecured. They increase the risk to the platform by providing additional attack vectors. User namespaces are used primarily for Linux containers. The value 0 disallows the use of user namespaces.
Warnings
warning  This configuration baseline was created to deploy the base operating system for general purpose workloads. When the operating system is configured for certain purposes, such as to host Linux Containers, it is expected that user.max_user_namespaces will be enabled. Note that this rule deliberately does not have remediations attached. Use the sysctl_user_max_user_namespaces if you want to utilize remediation for this rule.
OVAL test results details

user.max_user_namespaces static configuration  oval:ssg-test_sysctl_user_max_user_namespaces_no_remediation_static_user:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_user_max_user_namespaces_no_remediation:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_user_max_user_namespaces_no_remediation:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_user_max_user_namespaces_no_remediation:obj:1

user.max_user_namespaces static configuration  oval:ssg-test_sysctl_user_max_user_namespaces_no_remediation_static_user_missing:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_static_user_sysctl_user_max_user_namespaces_no_remediation:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_static_etc_lib_sysctls_sysctl_user_max_user_namespaces_no_remediation:obj:1 oval:ssg-object_static_run_usr_local_sysctls_sysctl_user_max_user_namespaces_no_remediation:obj:1

user.max_user_namespaces static configuration in /usr/lib/sysctl.d/*.conf  oval:ssg-test_sysctl_user_max_user_namespaces_no_remediation_static_pkg_correct:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_static_usr_lib_sysctld_sysctl_user_max_user_namespaces_no_remediation:obj:1 of type textfilecontent54_object
PathFilenamePatternInstance
/usr/lib/sysctl.d^.*\.conf$^[\s]*user.max_user_namespaces[\s]*=[\s]*(.*\S)[\s]*$1

kernel runtime parameter user.max_user_namespaces set to 0  oval:ssg-test_sysctl_user_max_user_namespaces_no_remediation_runtime:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameValue
falseuser.max_user_namespaces62806
Install policycoreutils Packagexccdf_org.ssgproject.content_rule_package_policycoreutils_installed lowCCE-82976-2

Install policycoreutils Package

Rule IDxccdf_org.ssgproject.content_rule_package_policycoreutils_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_policycoreutils_installed:def:1
Time2025-11-18T17:07:03-05:00
Severitylow
Identifiers:

CCE-82976-2

References:
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000134-GPOS-00068
stigidRHEL-08-010171
stigrefSV-230241r1017060_rule
Description
The policycoreutils package can be installed with the following command:
$ sudo yum install policycoreutils
Rationale
Security-enhanced Linux is a feature of the Linux kernel and a number of utilities with enhanced security functionality designed to add mandatory access controls to Linux. The Security-enhanced Linux kernel contains new architectural components originally developed to improve security of the Flask operating system. These architectural components provide general support for the enforcement of many kinds of mandatory access control policies, including those based on the concepts of Type Enforcement, Role-based Access Control, and Multi-level Security. policycoreutils contains the policy core utilities that are required for basic operation of an SELinux-enabled system. These utilities include load_policy to load SELinux policies, setfiles to label filesystems, newrole to switch roles, and so on.
OVAL test results details

package policycoreutils is installed  oval:ssg-test_package_policycoreutils_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedpolicycoreutilsx86_64(none)25.el82.90:2.9-25.el8199e2f91fd431d51policycoreutils-0:2.9-25.el8.x86_64
Elevate The SELinux Context When An Administrator Calls The Sudo Commandxccdf_org.ssgproject.content_rule_selinux_context_elevation_for_sudo mediumCCE-90668-5

Elevate The SELinux Context When An Administrator Calls The Sudo Command

Rule IDxccdf_org.ssgproject.content_rule_selinux_context_elevation_for_sudo
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-selinux_context_elevation_for_sudo:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-90668-5

References:
nistAC-3(4), AC-6(10)
os-srgSRG-OS-000324-GPOS-00125
stigidRHEL-08-010455
stigrefSV-272484r1069340_rule
Description
Configure the operating system to elevate the SELinux context when an administrator calls the sudo command. Edit a file in the /etc/sudoers.d directory with the following command:
sudo visudo -f /etc/sudoers.d/CUSTOM_FILE
Use the following example to build the CUSTOM_FILE in the /etc/sudoers.d directory to allow any administrator belonging to a designated sudoers admin group to elevate their SELinux context with the use of the sudo command:
%wheel ALL=(ALL) TYPE=sysadm_t ROLE=sysadm_r ALL
Rationale
Preventing non-privileged users from executing privileged functions mitigates the risk that unauthorized individuals or processes may gain unnecessary access to information or privileges.

Privileged functions include, for example, establishing accounts, performing system integrity checks, or administering cryptographic key management activities. Non-privileged users are individuals who do not possess appropriate authorizations. Circumventing intrusion detection and prevention mechanisms or malicious code protection mechanisms are examples of privileged functions that require protection from non-privileged users.
OVAL test results details

check correct configuration in /etc/sudoers and /etc/sudoers.d/*  oval:ssg-test_sudo_selinux_elevation_type:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sudo_selinux_elevation_type:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^\s*%\w+.*TYPE=(\w+).*$1

check correct configuration in /etc/sudoers and /etc/sudoers.d/*  oval:ssg-test_sudo_selinux_elevation_role:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sudo_selinux_elevation_role:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sudoers(\.d/.*)?$^\s*%\w+.*ROLE=(\w+).*$1
Configure SELinux Policyxccdf_org.ssgproject.content_rule_selinux_policytype mediumCCE-80868-3

Configure SELinux Policy

Rule IDxccdf_org.ssgproject.content_rule_selinux_policytype
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-selinux_policytype:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-80868-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 3, 4, 5, 6, 8, 9
cobit5APO01.06, APO11.04, APO13.01, BAI03.05, DSS01.05, DSS03.01, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06, MEA02.01
cui3.1.2, 3.7.2
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e)
isa-62443-20094.2.3.4, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.2, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-004-6 R3.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3, CIP-007-3 R6.5
nistAC-3, AC-3(3)(a), AU-9, SC-7(21)
nist-csfDE.AE-1, ID.AM-3, PR.AC-4, PR.AC-5, PR.AC-6, PR.DS-5, PR.PT-1, PR.PT-3, PR.PT-4
osppFMT_MOF_EXT.1
os-srgSRG-OS-000445-GPOS-00199
app-srg-ctrSRG-APP-000233-CTR-000585
anssiR46, R64
bsiAPP.4.4.A4, SYS.1.6.A3, SYS.1.6.A18, SYS.1.6.A21
cis1.5.1.3
pcidss41.2.6, 1.2
stigidRHEL-08-010450
stigrefSV-230282r958944_rule
Description
The SELinux targeted policy is appropriate for general-purpose desktops and servers, as well as systems in many other roles. To configure the system to use this policy, add or correct the following line in /etc/selinux/config:
SELINUXTYPE=targeted
Other policies, such as mls, provide additional security labeling and greater confinement but are not compatible with many general-purpose use cases.
Rationale
Setting the SELinux policy to targeted or a more specialized policy ensures the system will confine processes that are likely to be targeted for exploitation, such as network or system services.

Note: During the development or debugging of SELinux modules, it is common to temporarily place non-production systems in permissive mode. In such temporary cases, SELinux policies should be developed, and once work is completed, the system should be reconfigured to targeted.
OVAL test results details

tests the value of SELINUXTYPE setting in the /etc/selinux/config file  oval:ssg-test_selinux_policytype:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/selinux/configSELINUXTYPE=targeted

The configuration file /etc/selinux/config exists for selinux_policytype  oval:ssg-test_selinux_policytype_config_file_exists:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathTypeUIDGIDSize (B)Permissions
not evaluated/etc/selinux/configregular00548rw-r--r-- 
Ensure SELinux State is Enforcingxccdf_org.ssgproject.content_rule_selinux_state highCCE-80869-1

Ensure SELinux State is Enforcing

Rule IDxccdf_org.ssgproject.content_rule_selinux_state
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-selinux_state:def:1
Time2025-11-18T17:07:03-05:00
Severityhigh
Identifiers:

CCE-80869-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 3, 4, 5, 6, 8, 9
cobit5APO01.06, APO11.04, APO13.01, BAI03.05, DSS01.05, DSS03.01, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06, MEA02.01
cui3.1.2, 3.7.2
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e)
isa-62443-20094.2.3.4, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4, 4.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.2, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-004-6 R3.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3, CIP-007-3 R6.5
nistAC-3, AC-3(3)(a), AU-9, SC-7(21)
nist-csfDE.AE-1, ID.AM-3, PR.AC-4, PR.AC-5, PR.AC-6, PR.DS-5, PR.PT-1, PR.PT-3, PR.PT-4
osppFMT_MOF_EXT.1
os-srgSRG-OS-000445-GPOS-00199, SRG-OS-000134-GPOS-00068
anssiR37, R79
bsiAPP.4.4.A4, SYS.1.6.A3, SYS.1.6.A18, SYS.1.6.A21
cis1.5.1.5
pcidss41.2.6, 1.2
stigidRHEL-08-010170
stigrefSV-230240r1017059_rule
Description
The SELinux state should be set to enforcing at system boot time. In the file /etc/selinux/config, add or correct the following line to configure the system to boot into enforcing mode:
SELINUX=enforcing
Ensure that all files have correct SELinux labels by running:
fixfiles onboot
Then reboot the system.
Rationale
Setting the SELinux state to enforcing ensures SELinux is able to confine potentially compromised processes to the security policy, which is designed to prevent them from causing damage to the system or further elevating their privileges.
OVAL test results details

/selinux/enforce is 1  oval:ssg-test_etc_selinux_config:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/selinux/configSELINUX=enforcing
Uninstall Automatic Bug Reporting Tool (abrt)xccdf_org.ssgproject.content_rule_package_abrt_removed mediumCCE-80948-3

Uninstall Automatic Bug Reporting Tool (abrt)

Rule IDxccdf_org.ssgproject.content_rule_package_abrt_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_abrt_removed:def:1
Time2025-11-18T17:07:03-05:00
Severitymedium
Identifiers:

CCE-80948-3

References:
os-srgSRG-OS-000095-GPOS-00049
stigidRHEL-08-040001
stigrefSV-230488r1017272_rule
Description
The Automatic Bug Reporting Tool (abrt) collects and reports crash data when an application crash is detected. Using a variety of plugins, abrt can email crash reports to system administrators, log crash reports to files, or forward crash reports to a centralized issue tracking system such as RHTSupport. The abrt package can be removed with the following command:
$ sudo yum erase abrt
Rationale
Mishandling crash data could expose sensitive information about vulnerabilities in software executing on the system, as well as sensitive information from within a process's address space or registers.
OVAL test results details

package abrt is removed  oval:ssg-test_package_abrt_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_abrt_removed:obj:1 of type rpminfo_object
Name
abrt
Disable KDump Kernel Crash Analyzer (kdump)xccdf_org.ssgproject.content_rule_service_kdump_disabled mediumCCE-80878-2

Disable KDump Kernel Crash Analyzer (kdump)

Rule IDxccdf_org.ssgproject.content_rule_service_kdump_disabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_kdump_disabled:def:1
Time2025-11-18T17:07:06-05:00
Severitymedium
Identifiers:

CCE-80878-2

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3), 164.308(a)(4), 164.310(b), 164.310(c), 164.312(a), 164.312(e)
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
osppFMT_SMF_EXT.1.1
os-srgSRG-OS-000269-GPOS-00103, SRG-OS-000480-GPOS-00227
stigidRHEL-08-010670
stigrefSV-230310r1017120_rule
Description
The kdump service provides a kernel crash dump analyzer. It uses the kexec system call to boot a secondary kernel ("capture" kernel) following a system crash, which can load information from the crashed kernel for analysis. The kdump service can be disabled with the following command:
$ sudo systemctl mask --now kdump.service
Rationale
Kernel core dumps may contain the full contents of system memory at the time of the crash. Kernel core dumps consume a considerable amount of disk space and may result in denial of service by exhausting the available space on the target file system partition. Unless the system is used for kernel development or testing, there is little need to run the kdump service.

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
  "$SYSTEMCTL_EXEC" stop 'kdump.service'
fi
"$SYSTEMCTL_EXEC" disable 'kdump.service'
"$SYSTEMCTL_EXEC" mask 'kdump.service'
# Disable socket activation if we have a unit file for it
if "$SYSTEMCTL_EXEC" -q list-unit-files kdump.socket; then
    if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
      "$SYSTEMCTL_EXEC" stop 'kdump.socket'
    fi
    "$SYSTEMCTL_EXEC" mask 'kdump.socket'
fi
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
"$SYSTEMCTL_EXEC" reset-failed 'kdump.service' || true

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80878-2
  - DISA-STIG-RHEL-08-010670
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_kdump_disabled

- name: Disable KDump Kernel Crash Analyzer (kdump) - Disable service kdump
  block:

  - name: Disable KDump Kernel Crash Analyzer (kdump) - Collect systemd Services Present
      in the System
    ansible.builtin.command: systemctl -q list-unit-files --type service
    register: service_exists
    changed_when: false
    failed_when: service_exists.rc not in [0, 1]
    check_mode: false

  - name: Disable KDump Kernel Crash Analyzer (kdump) - Ensure kdump.service is Masked
    ansible.builtin.systemd:
      name: kdump.service
      state: stopped
      enabled: false
      masked: true
    when: service_exists.stdout_lines is search("kdump.service", multiline=True)

  - name: Unit Socket Exists - kdump.socket
    ansible.builtin.command: systemctl -q list-unit-files kdump.socket
    register: socket_file_exists
    changed_when: false
    failed_when: socket_file_exists.rc not in [0, 1]
    check_mode: false

  - name: Disable KDump Kernel Crash Analyzer (kdump) - Disable Socket kdump
    ansible.builtin.systemd:
      name: kdump.socket
      enabled: false
      state: stopped
      masked: true
    when: socket_file_exists.stdout_lines is search("kdump.socket", multiline=True)
  tags:
  - CCE-80878-2
  - DISA-STIG-RHEL-08-010670
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - disable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_kdump_disabled
  - special_service_block
  when: '"kernel" in ansible_facts.packages'

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include disable_kdump

class disable_kdump {
  service {'kdump':
    enable => false,
    ensure => 'stopped',
  }
}

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: kdump.service
        enabled: false
        mask: true
      - name: kdump.socket
        enabled: false
        mask: true


[customizations.services]
masked = ["kdump"]
OVAL test results details

package kexec-tools is removed  oval:ssg-service_kdump_disabled_test_service_kdump_package_kexec-tools_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedkexec-toolsx86_64(none)14.el82.0.260:2.0.26-14.el8199e2f91fd431d51kexec-tools-0:2.0.26-14.el8.x86_64

Test that the kdump service is not running  oval:ssg-test_service_not_running_service_kdump_disabled_kdump:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsekdump.serviceActiveStateactive

Test that the property LoadState from the service kdump is masked  oval:ssg-test_service_loadstate_is_masked_service_kdump_disabled_kdump:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsekdump.serviceLoadStateloaded

Test that the service kdump is not found  oval:ssg-test_service_not_found_service_kdump_disabled_kdump:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
falsekdump.serviceLoadStateloaded
Install fapolicyd Packagexccdf_org.ssgproject.content_rule_package_fapolicyd_installed mediumCCE-82191-8

Install fapolicyd Package

Rule IDxccdf_org.ssgproject.content_rule_package_fapolicyd_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_fapolicyd_installed:def:1
Time2025-11-18T17:07:06-05:00
Severitymedium
Identifiers:

CCE-82191-8

References:
nistCM-6(a), SI-4(22)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000370-GPOS-00155, SRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00230
stigidRHEL-08-040135
stigrefSV-230523r958804_rule
Description
The fapolicyd package can be installed with the following command:
$ sudo yum install fapolicyd
Rationale
fapolicyd (File Access Policy Daemon) implements application whitelisting to decide file access rights.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "fapolicyd" ; then
    yum install -y "fapolicyd"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82191-8
  - DISA-STIG-RHEL-08-040135
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-4(22)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_fapolicyd_installed

- name: Ensure fapolicyd is installed
  ansible.builtin.package:
    name: fapolicyd
    state: present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82191-8
  - DISA-STIG-RHEL-08-040135
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-4(22)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_fapolicyd_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_fapolicyd

class install_fapolicyd {
  package { 'fapolicyd':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=fapolicyd


[[packages]]
name = "fapolicyd"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install fapolicyd

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install fapolicyd
OVAL test results details

package fapolicyd is installed  oval:ssg-test_package_fapolicyd_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_fapolicyd_installed:obj:1 of type rpminfo_object
Name
fapolicyd
Enable the File Access Policy Servicexccdf_org.ssgproject.content_rule_service_fapolicyd_enabled mediumCCE-82249-4

Enable the File Access Policy Service

Rule IDxccdf_org.ssgproject.content_rule_service_fapolicyd_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_fapolicyd_enabled:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-82249-4

References:
nistCM-6(a), SI-4(22)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000370-GPOS-00155, SRG-OS-000368-GPOS-00154, SRG-OS-000480-GPOS-00230
stigidRHEL-08-040136
stigrefSV-244545r958804_rule
Description
The File Access Policy service should be enabled. The fapolicyd service can be enabled with the following command:
$ sudo systemctl enable fapolicyd.service
Rationale
The fapolicyd service (File Access Policy Daemon) implements application whitelisting to decide file access rights.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" unmask 'fapolicyd.service'
if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
  "$SYSTEMCTL_EXEC" start 'fapolicyd.service'
fi
"$SYSTEMCTL_EXEC" enable 'fapolicyd.service'

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82249-4
  - DISA-STIG-RHEL-08-040136
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-4(22)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_fapolicyd_enabled

- name: Enable the File Access Policy Service - Enable service fapolicyd
  block:

  - name: Gather the package facts
    ansible.builtin.package_facts:
      manager: auto

  - name: Enable the File Access Policy Service - Enable Service fapolicyd
    ansible.builtin.systemd:
      name: fapolicyd
      enabled: true
      state: started
      masked: false
    when:
    - '"fapolicyd" in ansible_facts.packages'
  tags:
  - CCE-82249-4
  - DISA-STIG-RHEL-08-040136
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SI-4(22)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_fapolicyd_enabled
  - special_service_block
  when: '"kernel" in ansible_facts.packages'

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include enable_fapolicyd

class enable_fapolicyd {
  service {'fapolicyd':
    enable => true,
    ensure => 'running',
  }
}


[customizations.services]
enabled = ["fapolicyd"]

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

service enable fapolicyd
OVAL test results details

package fapolicyd is installed  oval:ssg-test_service_fapolicyd_package_fapolicyd_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_service_fapolicyd_package_fapolicyd_installed:obj:1 of type rpminfo_object
Name
fapolicyd

Test that the fapolicyd service is running  oval:ssg-test_service_running_fapolicyd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_service_running_fapolicyd:obj:1 of type systemdunitproperty_object
UnitProperty
^fapolicyd\.(socket|service)$ActiveState

systemd test  oval:ssg-test_multi_user_wants_fapolicyd:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_fapolicyd_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs.xccdf_org.ssgproject.content_rule_fapolicy_default_deny mediumCCE-86478-5

Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy to Allow the Execution of Authorized Software Programs.

Rule IDxccdf_org.ssgproject.content_rule_fapolicy_default_deny
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-fapolicy_default_deny:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-86478-5

References:
nistCM-7 (2), CM-7 (5) (b), CM-6 b
os-srgSRG-OS-000368-GPOS-00154, SRG-OS-000370-GPOS-00155, SRG-OS-000480-GPOS-00232
stigidRHEL-08-040137
stigrefSV-244546r1017349_rule
Description
The Fapolicy module must be configured to employ a deny-all, permit-by-exception policy to allow the execution of authorized software programs and to prevent unauthorized software from running.
Rationale
Utilizing a whitelist provides a configuration management method for allowing the execution of only authorized software. Using only authorized software decreases risk by limiting the number of potential vulnerabilities. Verification of whitelisted software occurs prior to execution or at system startup. Proceed with caution with enforcing the use of this daemon. Improper configuration may render the system non-functional. The "fapolicyd" API is not namespace aware and can cause issues when launching or running containers.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

cat > /etc/fapolicyd/rules.d/99-deny-everything.rules << EOF
# Red Hat KCS 7003854 (https://access.redhat.com/solutions/7003854)
deny perm=any all : all
EOF

chmod 644 /etc/fapolicyd/rules.d/99-deny-everything.rules
chgrp fapolicyd /etc/fapolicyd/rules.d/99-deny-everything.rules

if [ -e "/etc/fapolicyd/fapolicyd.conf" ] ; then
    
    LC_ALL=C sed -i "/^\s*permissive\s*=\s*/Id" "/etc/fapolicyd/fapolicyd.conf"
else
    touch "/etc/fapolicyd/fapolicyd.conf"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/fapolicyd/fapolicyd.conf"

cp "/etc/fapolicyd/fapolicyd.conf" "/etc/fapolicyd/fapolicyd.conf.bak"
# Insert at the end of the file
printf '%s\n' "permissive = 0" >> "/etc/fapolicyd/fapolicyd.conf"
# Clean up after ourselves.
rm "/etc/fapolicyd/fapolicyd.conf.bak"

systemctl restart fapolicyd

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86478-5
  - DISA-STIG-RHEL-08-040137
  - NIST-800-53-CM-6 b
  - NIST-800-53-CM-7 (2)
  - NIST-800-53-CM-7 (5) (b)
  - fapolicy_default_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy
    to Allow the Execution of Authorized Software Programs. - Gather the package facts
  ansible.builtin.package_facts:
    manager: auto
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86478-5
  - DISA-STIG-RHEL-08-040137
  - NIST-800-53-CM-6 b
  - NIST-800-53-CM-7 (2)
  - NIST-800-53-CM-7 (5) (b)
  - fapolicy_default_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy
    to Allow the Execution of Authorized Software Programs. - Ensure a Final Rule
    Denying Everything
  ansible.builtin.copy:
    content: |
      # Red Hat KCS 7003854 (https://access.redhat.com/solutions/7003854)
      deny perm=any all : all
    dest: /etc/fapolicyd/rules.d/99-deny-everything.rules
    owner: root
    group: fapolicyd
    mode: '0644'
  when:
  - '"kernel" in ansible_facts.packages'
  - '"fapolicyd" in ansible_facts.packages'
  register: result_fapolicyd_final_rule
  tags:
  - CCE-86478-5
  - DISA-STIG-RHEL-08-040137
  - NIST-800-53-CM-6 b
  - NIST-800-53-CM-7 (2)
  - NIST-800-53-CM-7 (5) (b)
  - fapolicy_default_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy
    to Allow the Execution of Authorized Software Programs. - Ensure fapolicyd is
    Not Permissive
  ansible.builtin.lineinfile:
    path: /etc/fapolicyd/fapolicyd.conf
    regexp: ^(permissive\s*=).*$
    line: \1 0
    backrefs: true
  when:
  - '"kernel" in ansible_facts.packages'
  - '"fapolicyd" in ansible_facts.packages'
  register: result_fapolicyd_enforced
  tags:
  - CCE-86478-5
  - DISA-STIG-RHEL-08-040137
  - NIST-800-53-CM-6 b
  - NIST-800-53-CM-7 (2)
  - NIST-800-53-CM-7 (5) (b)
  - fapolicy_default_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Fapolicy Module to Employ a Deny-all, Permit-by-exception Policy
    to Allow the Execution of Authorized Software Programs. - Restart fapolicyd If
    Permissive Mode or Final Rule is Changed
  ansible.builtin.service:
    name: fapolicyd
    state: restarted
  when:
  - '"kernel" in ansible_facts.packages'
  - '"fapolicyd" in ansible_facts.packages'
  - result_fapolicyd_final_rule is changed or result_fapolicyd_enforced is changed
  tags:
  - CCE-86478-5
  - DISA-STIG-RHEL-08-040137
  - NIST-800-53-CM-6 b
  - NIST-800-53-CM-7 (2)
  - NIST-800-53-CM-7 (5) (b)
  - fapolicy_default_deny
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

fapolicyd employs a deny-all policy in compiled.rules file  oval:ssg-test_fapolicy_default_deny_policy_with_rulesd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_fapolicy_default_deny_policy_compiled_rules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fapolicyd/compiled.rules^\s*deny\s*perm=any\s*all\s*:\s*all\s*\z1

fapolicyd employs a deny-all policy in fapolicyd.rules file  oval:ssg-test_fapolicy_default_deny_policy_without_rulesd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_fapolicy_default_deny_policy_fapolicyd_rules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fapolicyd/fapolicyd.rules^\s*deny\s*perm=any\s*all\s*:\s*all\s*\z1

permissive mode is disabled in fapolicyd settings  oval:ssg-test_fapolicy_default_deny_enforcement:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_fapolicy_default_deny_permissive_mode:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/fapolicyd/fapolicyd.conf^\s*permissive\s*=\s*(\d+)1
Uninstall vsftpd Packagexccdf_org.ssgproject.content_rule_package_vsftpd_removed highCCE-82414-4

Uninstall vsftpd Package

Rule IDxccdf_org.ssgproject.content_rule_package_vsftpd_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_vsftpd_removed:def:1
Time2025-11-18T17:07:11-05:00
Severityhigh
Identifiers:

CCE-82414-4

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a), IA-5(1)(c), IA-5(1).1(v), CM-7, CM-7.1(ii)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000074-GPOS-00042, SRG-OS-000095-GPOS-00049, SRG-OS-000480-GPOS-00227
cis2.2.7
stigidRHEL-08-040360
stigrefSV-230558r1017320_rule
Description
The vsftpd package can be removed with the following command:
 $ sudo yum erase vsftpd
Rationale
Removing the vsftpd package decreases the risk of its accidental activation.
OVAL test results details

package vsftpd is removed  oval:ssg-test_package_vsftpd_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_vsftpd_removed:obj:1 of type rpminfo_object
Name
vsftpd
Remove the Kerberos Server Packagexccdf_org.ssgproject.content_rule_package_krb5-server_removed mediumCCE-85887-8

Remove the Kerberos Server Package

Rule IDxccdf_org.ssgproject.content_rule_package_krb5-server_removed
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-85887-8

References:
nistIA-7, IA-7.1
os-srgSRG-OS-000120-GPOS-00061
stigidRHEL-08-010163
stigrefSV-237640r1017323_rule
Description
The krb5-server package should be removed if not in use. Is this system the Kerberos server? If not, remove the package. The krb5-server package can be removed with the following command:
$ sudo yum erase krb5-server
The krb5-server RPM is not installed by default on a Red Hat Enterprise Linux 8 system. It is needed only by the Kerberos servers, not by the clients which use Kerberos for authentication. If the system is not intended for use as a Kerberos Server it should be removed.
Rationale
Unnecessary packages should not be installed to decrease the attack surface of the system. While this software is clearly essential on an KDC server, it is not necessary on typical desktop or workstation systems.
Disable Kerberos by removing host keytabxccdf_org.ssgproject.content_rule_kerberos_disable_no_keytab mediumCCE-82175-1

Disable Kerberos by removing host keytab

Rule IDxccdf_org.ssgproject.content_rule_kerberos_disable_no_keytab
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-82175-1

References:
ism0418, 1055, 1402
os-srgSRG-OS-000120-GPOS-00061
stigidRHEL-08-010161
stigrefSV-230238r1017057_rule
Description
Kerberos may rely on key distribution functions unapproved by Common Criteria. To prevent using Kerberos by system daemons, remove the Kerberos keytab files, especially /etc/krb5.keytab.
Rationale
Some key derivation functions (KDF) in Kerberos are not FIPS-compatible
Configure System to Forward All Mail From Postmaster to The Root Accountxccdf_org.ssgproject.content_rule_postfix_client_configure_mail_alias_postmaster mediumCCE-89063-2

Configure System to Forward All Mail From Postmaster to The Root Account

Rule IDxccdf_org.ssgproject.content_rule_postfix_client_configure_mail_alias_postmaster
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-postfix_client_configure_mail_alias_postmaster:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-89063-2

References:
nistAU-5(a), AU-5.1(ii)
os-srgSRG-OS-000046-GPOS-00022
stigidRHEL-08-030030
stigrefSV-230389r1017197_rule
Description
Verify the administrators are notified in the event of an audit processing failure. Check that the "/etc/aliases" file has a defined value for "root".
$ sudo grep "postmaster:\s*root$" /etc/aliases

postmaster: root
Rationale
It is critical for the appropriate personnel to be aware if a system is at risk of failing to process audit logs as required. Without this notification, the security personnel may be unaware of an impending failure of the audit capability, and system operation may be adversely affected. Audit processing failures include software/hardware errors, failures in the audit capturing mechanisms, and audit storage capacity being reached or exceeded.
OVAL test results details

Check if postmaster has the correct mail alias  oval:ssg-test_postfix_client_configure_mail_alias_postmaster:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/aliasespostmaster: root
Prevent Unrestricted Mail Relayingxccdf_org.ssgproject.content_rule_postfix_prevent_unrestricted_relay mediumCCE-84054-6

Prevent Unrestricted Mail Relaying

Rule IDxccdf_org.ssgproject.content_rule_postfix_prevent_unrestricted_relay
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-84054-6

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040290
stigrefSV-230550r1017312_rule
Description
Modify the
/etc/postfix/main.cf
file to restrict client connections to the local network with the following command:
$ sudo postconf -e 'smtpd_client_restrictions = permit_mynetworks,reject'
Rationale
If unrestricted mail relaying is permitted, unauthorized senders could use this host as a mail relay for the purpose of sending spam or other unauthorized activity.
The mailx Package Is Installedxccdf_org.ssgproject.content_rule_package_mailx_installed mediumCCE-87036-0

The mailx Package Is Installed

Rule IDxccdf_org.ssgproject.content_rule_package_mailx_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_mailx_installed:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-87036-0

References:
nistCM-3(5)
os-srgSRG-OS-000363-GPOS-00150
stigidRHEL-08-010358
stigrefSV-256974r1069321_rule
Description
A mail server is required for sending emails. The mailx package can be installed with the following command:
$ sudo yum install mailx
Rationale
Emails can be used to notify designated personnel about important system events such as failures or warnings.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "mailx" ; then
    yum install -y "mailx"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-87036-0
  - DISA-STIG-RHEL-08-010358
  - NIST-800-53-CM-3(5)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_mailx_installed

- name: Ensure mailx is installed
  ansible.builtin.package:
    name: mailx
    state: present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-87036-0
  - DISA-STIG-RHEL-08-010358
  - NIST-800-53-CM-3(5)
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_mailx_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_mailx

class install_mailx {
  package { 'mailx':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=mailx


[[packages]]
name = "mailx"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install mailx

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install mailx
OVAL test results details

package mailx is installed  oval:ssg-test_package_mailx_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_mailx_installed:obj:1 of type rpminfo_object
Name
mailx
The Postfix package is installedxccdf_org.ssgproject.content_rule_package_postfix_installed mediumCCE-85983-5

The Postfix package is installed

Rule IDxccdf_org.ssgproject.content_rule_package_postfix_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_postfix_installed:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-85983-5

References:
os-srgSRG-OS-000046-GPOS-00022
stigidRHEL-08-030030
stigrefSV-230389r1017197_rule
Description
A mail server is required for sending emails. The postfix package can be installed with the following command:
$ sudo yum install postfix
Rationale
Emails can be used to notify designated personnel about important system events such as failures or warnings.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if ! rpm -q --quiet "postfix" ; then
    yum install -y "postfix"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-85983-5
  - DISA-STIG-RHEL-08-030030
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_postfix_installed

- name: Ensure postfix is installed
  ansible.builtin.package:
    name: postfix
    state: present
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-85983-5
  - DISA-STIG-RHEL-08-030030
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_postfix_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_postfix

class install_postfix {
  package { 'postfix':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=postfix


[[packages]]
name = "postfix"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install postfix

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install postfix
OVAL test results details

package postfix is installed  oval:ssg-test_package_postfix_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_postfix_installed:obj:1 of type rpminfo_object
Name
postfix
Uninstall Sendmail Packagexccdf_org.ssgproject.content_rule_package_sendmail_removed mediumCCE-81039-0

Uninstall Sendmail Package

Rule IDxccdf_org.ssgproject.content_rule_package_sendmail_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_sendmail_removed:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-81039-0

References:
cis-csc11, 14, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000095-GPOS-00049
anssiR62
stigidRHEL-08-040002
stigrefSV-230489r1017273_rule
Description
Sendmail is not the default mail transfer agent and is not installed by default. The sendmail package can be removed with the following command:
$ sudo yum erase sendmail
Rationale
The sendmail software was not developed with security in mind and its design prevents it from being effectively contained by SELinux. Postfix should be used instead.
OVAL test results details

package sendmail is removed  oval:ssg-test_package_sendmail_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_sendmail_removed:obj:1 of type rpminfo_object
Name
sendmail
Mount Remote Filesystems with nodevxccdf_org.ssgproject.content_rule_mount_option_nodev_remote_filesystems mediumCCE-84052-0

Mount Remote Filesystems with nodev

Rule IDxccdf_org.ssgproject.content_rule_mount_option_nodev_remote_filesystems
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-84052-0

References:
cis-csc11, 13, 14, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS05.06, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6
iso27001-2013A.11.2.9, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.1, A.8.2.2, A.8.2.3, A.8.3.1, A.8.3.3, A.9.1.2
nistCM-6(a), MP-2
nist-csfPR.IP-1, PR.PT-2, PR.PT-3
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010640
stigrefSV-230307r1017117_rule
Description
Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.
Rationale
Legitimate device files should only exist in the /dev directory. NFS mounts should not present device files to users.
Mount Remote Filesystems with noexecxccdf_org.ssgproject.content_rule_mount_option_noexec_remote_filesystems mediumCCE-84050-4

Mount Remote Filesystems with noexec

Rule IDxccdf_org.ssgproject.content_rule_mount_option_noexec_remote_filesystems
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-84050-4

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistAC-6, AC-6(8), AC-6(10), CM-6(a)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010630
stigrefSV-230306r1017116_rule
Description
Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.
Rationale
The noexec mount option causes the system not to execute binary files. This option must be used for mounting any file system not containing approved binary files as they may be incompatible. Executing files from untrusted file systems increases the opportunity for unprivileged users to attain unauthorized administrative access.
Mount Remote Filesystems with nosuidxccdf_org.ssgproject.content_rule_mount_option_nosuid_remote_filesystems mediumCCE-84053-8

Mount Remote Filesystems with nosuid

Rule IDxccdf_org.ssgproject.content_rule_mount_option_nosuid_remote_filesystems
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-84053-8

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistAC-6, AC-6(1), CM6(a)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010650
stigrefSV-230308r1017118_rule
Description
Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of any NFS mounts.
Rationale
NFS mounts should not present suid binaries to users. Only vendor-supplied suid executables should be installed to their default location on the local filesystem.
A remote time server for Chrony is configuredxccdf_org.ssgproject.content_rule_chronyd_specify_remote_server mediumCCE-82873-1

A remote time server for Chrony is configured

Rule IDxccdf_org.ssgproject.content_rule_chronyd_specify_remote_server
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-chronyd_specify_remote_server:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-82873-1

References:
ism0988, 1405
nistCM-6(a), AU-8(1)(a)
pcidssReq-10.4.3
os-srgSRG-OS-000355-GPOS-00143
anssiR71
cis2.1.2
pcidss410.6.2, 10.6
stigidRHEL-08-030740
stigrefSV-230484r1038944_rule
Description
Chrony is a daemon which implements the Network Time Protocol (NTP). It is designed to synchronize system clocks across a variety of systems and use a source that is highly accurate. More information on chrony can be found at https://chrony-project.org/. Chrony can be configured to be a client and/or a server. Add or edit server or pool lines to /etc/chrony.conf as appropriate:
server <remote-server>
Multiple servers may be configured.
Rationale
If chrony is in use on the system proper configuration is vital to ensuring time synchronization is working properly.
OVAL test results details

Ensure at least one NTP server is set  oval:ssg-test_chronyd_remote_server:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/chrony.confpool 2.rhel.pool.ntp.org iburst
Disable chrony daemon from acting as serverxccdf_org.ssgproject.content_rule_chronyd_client_only lowCCE-82988-7

Disable chrony daemon from acting as server

Rule IDxccdf_org.ssgproject.content_rule_chronyd_client_only
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-chronyd_client_only:def:1
Time2025-11-18T17:07:11-05:00
Severitylow
Identifiers:

CCE-82988-7

References:
nistAU-8(1), AU-12(1)
osppFMT_SMF_EXT.1
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000095-GPOS-00049
stigidRHEL-08-030741
stigrefSV-230485r1017269_rule
Description
The port option in /etc/chrony.conf can be set to 0 to make chrony daemon to never open any listening port for server operation and to operate strictly in a client-only mode.
Rationale
In order to prevent unauthorized connection of devices, unauthorized transfer of information, or unauthorized tunneling (i.e., embedding of data types within data types), organizations must disable or restrict unused or unnecessary physical and logical ports/protocols on information systems. Operating systems are capable of providing a wide variety of functions and services. Some of the functions and services provided by default may not be necessary to support essential organizational operations. Additionally, it is sometimes convenient to provide multiple services from a single component (e.g., VPN and IPS); however, doing so increases risk over limiting the services provided by any one component. To support the requirements and principles of least functionality, the operating system must support the organizational requirements, providing only essential capabilities and limiting the use of ports, protocols, and/or services to only those required, authorized, and approved to conduct official business or to address authorized quality of life issues.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q chrony; }; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^port")

# shellcheck disable=SC2059
printf -v formatted_output "%s %s" "$stripped_key" "0"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^port\\>" "/etc/chrony.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^port\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf"
else
    if [[ -s "/etc/chrony.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/chrony.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/chrony.conf"
    fi
    cce="CCE-82988-7"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/chrony.conf" >> "/etc/chrony.conf"
    printf '%s\n' "$formatted_output" >> "/etc/chrony.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82988-7
  - DISA-STIG-RHEL-08-030741
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)
  - chronyd_client_only
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable chrony daemon from acting as server
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*port\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/chrony.conf
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*port\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/chrony.conf
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*port\s+
      line: port 0
      state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"chrony" in ansible_facts.packages'
  tags:
  - CCE-82988-7
  - DISA-STIG-RHEL-08-030741
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)
  - chronyd_client_only
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }}
        mode: 420
        overwrite: true
        path: /etc/chrony.conf
      - contents:
          source: data:,
        mode: 420
        overwrite: true
        path: /etc/chrony.d/.mco-keep
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }}
        mode: 420
        overwrite: true
        path: /etc/chrony.d/ntp-server.conf
OVAL test results details

check if port is 0 in /etc/chrony.conf  oval:ssg-test_chronyd_client_only:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_chronyd_port_value:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/chrony.conf^\s*port[\s]+(\S+)1
Disable network management of chrony daemonxccdf_org.ssgproject.content_rule_chronyd_no_chronyc_network lowCCE-82840-0

Disable network management of chrony daemon

Rule IDxccdf_org.ssgproject.content_rule_chronyd_no_chronyc_network
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-chronyd_no_chronyc_network:def:1
Time2025-11-18T17:07:11-05:00
Severitylow
Identifiers:

CCE-82840-0

References:
nistCM-7(1)
os-srgSRG-OS-000096-GPOS-00050, SRG-OS-000095-GPOS-00049
stigidRHEL-08-030742
stigrefSV-230486r1017270_rule
Description
The cmdport option in /etc/chrony.conf can be set to 0 to stop chrony daemon from listening on the UDP port 323 for management connections made by chronyc.
Rationale
Minimizing the exposure of the server functionality of the chrony daemon diminishes the attack surface.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q chrony; }; then

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^cmdport")

# shellcheck disable=SC2059
printf -v formatted_output "%s %s" "$stripped_key" "0"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^cmdport\\>" "/etc/chrony.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^cmdport\\>.*/$escaped_formatted_output/gi" "/etc/chrony.conf"
else
    if [[ -s "/etc/chrony.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/chrony.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/chrony.conf"
    fi
    cce="CCE-82840-0"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/chrony.conf" >> "/etc/chrony.conf"
    printf '%s\n' "$formatted_output" >> "/etc/chrony.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82840-0
  - DISA-STIG-RHEL-08-030742
  - NIST-800-53-CM-7(1)
  - chronyd_no_chronyc_network
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

- name: Disable network management of chrony daemon
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*cmdport\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/chrony.conf
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*cmdport\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/chrony.conf
    ansible.builtin.lineinfile:
      path: /etc/chrony.conf
      create: true
      regexp: (?i)^\s*cmdport\s+
      line: cmdport 0
      state: present
  when:
  - '"kernel" in ansible_facts.packages'
  - '"chrony" in ansible_facts.packages'
  tags:
  - CCE-82840-0
  - DISA-STIG-RHEL-08-030742
  - NIST-800-53-CM-7(1)
  - chronyd_no_chronyc_network
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }}
        mode: 420
        overwrite: true
        path: /etc/chrony.conf
      - contents:
          source: data:,
        mode: 420
        overwrite: true
        path: /etc/chrony.d/.mco-keep
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }}
        mode: 420
        overwrite: true
        path: /etc/chrony.d/ntp-server.conf
OVAL test results details

check if cmdport is 0 in /etc/chrony.conf  oval:ssg-test_chronyd_no_chronyc_network:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_chronyd_cmdport_value:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/chrony.conf^\s*cmdport[\s]+(\S+)1
Configure Time Service Maxpoll Intervalxccdf_org.ssgproject.content_rule_chronyd_or_ntpd_set_maxpoll mediumCCE-84059-5

Configure Time Service Maxpoll Interval

Rule IDxccdf_org.ssgproject.content_rule_chronyd_or_ntpd_set_maxpoll
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-chronyd_or_ntpd_set_maxpoll:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-84059-5

References:
cis-csc1, 14, 15, 16, 3, 5, 6
cobit5APO11.04, BAI03.05, DSS05.04, DSS05.07, MEA02.01
isa-62443-20094.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1
nistCM-6(a), AU-8(1)(b), AU-12(1)
nist-csfPR.PT-1
os-srgSRG-OS-000355-GPOS-00143, SRG-OS-000356-GPOS-00144, SRG-OS-000359-GPOS-00146
stigidRHEL-08-030740
stigrefSV-230484r1038944_rule
Description
The maxpoll should be configured to 16 in /etc/ntp.conf or /etc/chrony.conf (or /etc/chrony.d/) to continuously poll time servers. To configure maxpoll in /etc/ntp.conf or /etc/chrony.conf (or /etc/chrony.d/) add the following after each server, pool or peer entry:
maxpoll 16
to server directives. If using chrony, any pool directives should be configured too.
Rationale
Inaccurate time stamps make it more difficult to correlate events and can lead to an inaccurate analysis. Determining the correct time a particular event occurred on a system is critical when conducting forensic analysis and investigating system events. Sources outside the configured acceptable allowance (drift) may be inaccurate. Synchronizing internal information system clocks provides uniformity of time stamps for information systems with multiple system clocks and systems connected over a network. Organizations should consider endpoints that may not have regular access to the authoritative time server (e.g., mobile, teleworking, and tactical endpoints).

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { ( rpm --quiet -q chrony || rpm --quiet -q ntp ); }; then

var_time_service_set_maxpoll='16'




pof="/usr/sbin/pidof"


CONFIG_FILES="/etc/ntp.conf"
$pof ntpd || {
    CHRONY_D_PATH=/etc/chrony.d/
    
    mapfile -t CONFIG_FILES < <(find ${CHRONY_D_PATH}.* -type f -name '*.conf')
    
    CONFIG_FILES+=(/etc/chrony.conf)
}

# get list of ntp files

for config_file in "${CONFIG_FILES[@]}" ; do
    # Set maxpoll values to var_time_service_set_maxpoll
    sed -i "s/^\(\(server\|pool\|peer\).*maxpoll\) [0-9,-][0-9]*\(.*\)$/\1 $var_time_service_set_maxpoll \3/" "$config_file"
done

for config_file in "${CONFIG_FILES[@]}" ; do
    # Add maxpoll to server, pool or peer entries without maxpoll
    grep "^\(server\|pool\|peer\)" "$config_file" | grep -v maxpoll | while read -r line ; do
        sed -i "s/$line/& maxpoll $var_time_service_set_maxpoll/" "$config_file"
    done
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_time_service_set_maxpoll # promote to variable
  set_fact:
    var_time_service_set_maxpoll: !!str 16
  tags:
    - always

- name: Configure Time Service Maxpoll Interval - Check That /etc/ntp.conf Exist
  ansible.builtin.stat:
    path: /etc/ntp.conf
  register: ntp_conf_exist_result
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Update the maxpoll Values in /etc/ntp.conf
  ansible.builtin.replace:
    path: /etc/ntp.conf
    regexp: ^(server.*maxpoll)[ ]+[0-9]+(.*)$
    replace: \1 {{ var_time_service_set_maxpoll }}\2
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - ntp_conf_exist_result.stat.exists
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Set the maxpoll Values in /etc/ntp.conf
  ansible.builtin.replace:
    path: /etc/ntp.conf
    regexp: (^server\s+((?!maxpoll).)*)$
    replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - ntp_conf_exist_result.stat.exists
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Check That /etc/chrony.conf Exist
  ansible.builtin.stat:
    path: /etc/chrony.conf
  register: chrony_conf_exist_result
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Update the maxpoll Values in /etc/chrony.conf
  ansible.builtin.replace:
    path: /etc/chrony.conf
    regexp: ^((?:server|pool|peer).*maxpoll)[ ]+[0-9]+(.*)$
    replace: \1 {{ var_time_service_set_maxpoll }}\2
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - chrony_conf_exist_result.stat.exists
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Set the maxpoll Values in /etc/chrony.conf
  ansible.builtin.replace:
    path: /etc/chrony.conf
    regexp: (^(?:server|pool|peer)\s+((?!maxpoll).)*)$
    replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - chrony_conf_exist_result.stat.exists
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Get Conf Files from /etc/chrony.d/
  ansible.builtin.find:
    path: /etc/chrony.d/
    patterns: '*.conf'
    file_type: file
  register: chrony_d_conf_files
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Update the maxpoll Values in /etc/chrony.d/
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: ^((?:server|pool|peer).*maxpoll)[ ]+[0-9,-]+(.*)$
    replace: \1 {{ var_time_service_set_maxpoll }}\2
  loop: '{{ chrony_d_conf_files.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - chrony_d_conf_files.matched
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Configure Time Service Maxpoll Interval - Set the maxpoll Values in /etc/chrony.d/
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: (^(?:server|pool|peer)\s+((?!maxpoll).)*)$
    replace: \1 maxpoll {{ var_time_service_set_maxpoll }}\n
  loop: '{{ chrony_d_conf_files.files }}'
  when:
  - '"kernel" in ansible_facts.packages'
  - ( "chrony" in ansible_facts.packages or "ntp" in ansible_facts.packages )
  - chrony_d_conf_files.matched
  tags:
  - CCE-84059-5
  - DISA-STIG-RHEL-08-030740
  - NIST-800-53-AU-12(1)
  - NIST-800-53-AU-8(1)(b)
  - NIST-800-53-CM-6(a)
  - chronyd_or_ntpd_set_maxpoll
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%20Allow%20for%20extra%20configuration%20files.%20This%20is%20useful%0A%23%20for%20admins%20specifying%20their%20own%20NTP%20servers%0Ainclude%20/etc/chrony.d/%2A.conf%0A%0A%23%20Set%20chronyd%20as%20client-only.%0Aport%200%0A%0A%23%20Disable%20chronyc%20from%20the%20network%0Acmdport%200%0A%0A%23%20Record%20the%20rate%20at%20which%20the%20system%20clock%20gains/losses%20time.%0Adriftfile%20/var/lib/chrony/drift%0A%0A%23%20Allow%20the%20system%20clock%20to%20be%20stepped%20in%20the%20first%20three%20updates%0A%23%20if%20its%20offset%20is%20larger%20than%201%20second.%0Amakestep%201.0%203%0A%0A%23%20Enable%20kernel%20synchronization%20of%20the%20real-time%20clock%20%28RTC%29.%0Artcsync%0A%0A%23%20Enable%20hardware%20timestamping%20on%20all%20interfaces%20that%20support%20it.%0A%23hwtimestamp%20%2A%0A%0A%23%20Increase%20the%20minimum%20number%20of%20selectable%20sources%20required%20to%20adjust%0A%23%20the%20system%20clock.%0A%23minsources%202%0A%0A%23%20Allow%20NTP%20client%20access%20from%20local%20network.%0A%23allow%20192.168.0.0/16%0A%0A%23%20Serve%20time%20even%20if%20not%20synchronized%20to%20a%20time%20source.%0A%23local%20stratum%2010%0A%0A%23%20Require%20authentication%20%28nts%20or%20key%20option%29%20for%20all%20NTP%20sources.%0A%23authselectmode%20require%0A%0A%23%20Specify%20file%20containing%20keys%20for%20NTP%20authentication.%0Akeyfile%20/etc/chrony.keys%0A%0A%23%20Insert/delete%20leap%20seconds%20by%20slewing%20instead%20of%20stepping.%0A%23leapsecmode%20slew%0A%0A%23%20Get%20TAI-UTC%20offset%20and%20leap%20seconds%20from%20the%20system%20tz%20database.%0Aleapsectz%20right/UTC%0A%0A%23%20Specify%20directory%20for%20log%20files.%0Alogdir%20/var/log/chrony%0A%0A%23%20Select%20which%20information%20is%20logged.%0A%23log%20measurements%20statistics%20tracking }}
        mode: 420
        overwrite: true
        path: /etc/chrony.conf
      - contents:
          source: data:,
        mode: 420
        overwrite: true
        path: /etc/chrony.d/.mco-keep
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20ntp%20server%0A%23%20%7B%7B.var_multiple_time_servers%7D%7D%20we%20have%20to%20put%20variable%20array%20name%20here%20for%20mutilines%20remediation%0A%7B%7B%24var_time_service_set_maxpoll%3A%3D.var_time_service_set_maxpoll%7D%7D%0A%7B%7Brange%20%24element%3A%3D.var_multiple_time_servers%7CtoArrayByComma%7D%7Dserver%20%7B%7B%24element%7D%7D%20minpoll%204%20maxpoll%20%7B%7B%24var_time_service_set_maxpoll%7D%7D%0A%7B%7Bend%7D%7D }}
        mode: 420
        overwrite: true
        path: /etc/chrony.d/ntp-server.conf
OVAL test results details

check if maxpoll is set in /etc/ntp.conf  oval:ssg-test_ntp_set_maxpoll:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ntp_set_maxpoll:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ntp.conf^server[\s]+[\S]+.*maxpoll[\s]+(\d+)1

check if all server entries have maxpoll set in /etc/ntp.conf  oval:ssg-test_ntp_all_server_has_maxpoll:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_ntp_all_server_has_maxpoll:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ntp.conf^server[\s]+[\S]+[\s]+(.*)1

check if maxpoll is set in /etc/chrony.conf or /etc/chrony.d/  oval:ssg-test_chrony_set_maxpoll:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_chrony_set_maxpoll:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^(/etc/chrony\.conf|/etc/chrony\.d/.+\.conf)$^(?:server|pool|peer)[\s]+[\S]+.*maxpoll[\s]+(\d+)1

check if all server entries have maxpoll set in /etc/chrony.conf or /etc/chrony.d/  oval:ssg-test_chrony_all_server_has_maxpoll:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/chrony.confpool 2.rhel.pool.ntp.org iburst
Ensure Chrony is only configured with the server directivexccdf_org.ssgproject.content_rule_chronyd_server_directive mediumCCE-86077-5

Ensure Chrony is only configured with the server directive

Rule IDxccdf_org.ssgproject.content_rule_chronyd_server_directive
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-chronyd_server_directive:def:1
Time2025-11-18T17:07:11-05:00
Severitymedium
Identifiers:

CCE-86077-5

References:
os-srgSRG-OS-000355-GPOS-00143, SRG-OS-000356-GPOS-00144, SRG-OS-000359-GPOS-00146
stigidRHEL-08-030740
stigrefSV-230484r1038944_rule
Description
Check that Chrony only has time sources configured with the server directive.
Rationale
Depending on the infrastructure being used the pool directive may not be supported. Using the server directive allows for better control of where the system gets time data from.
Warnings
warning  This rule doesn't come with a remediation, the time source needs to be added by the administrator.
OVAL test results details

Ensure at least one time source is set with server directive  oval:ssg-test_chronyd_server_directive_with_server:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_chronyd_server_directive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^(/etc/chrony\.conf|/etc/chrony\.d/.+\.conf)$^[\s]*server.*$1

Ensure no time source is set with pool directive  oval:ssg-test_chronyd_server_directive_no_pool:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_chronyd_no_pool_directive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^(/etc/chrony\.conf|/etc/chrony\.d/.+\.conf)$^[\s]+pool.*$1
Remove Host-Based Authentication Filesxccdf_org.ssgproject.content_rule_no_host_based_files highCCE-84055-3

Remove Host-Based Authentication Files

Rule IDxccdf_org.ssgproject.content_rule_no_host_based_files
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-no_host_based_files:def:1
Time2025-11-18T17:07:13-05:00
Severityhigh
Identifiers:

CCE-84055-3

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010460
stigrefSV-230283r1017094_rule
Description
The shosts.equiv file lists remote hosts and users that are trusted by the local system. To remove these files, run the following command to delete them from any location:
$ sudo rm /[path]/[to]/[file]/shosts.equiv
Rationale
The shosts.equiv files are used to configure host-based authentication for the system via SSH. Host-based authentication is not sufficient for preventing unauthorized access to the system, as it does not require interactive identification and authentication of a connection request, or for the use of two-factor authentication.
OVAL test results details

look for shosts.equiv in /  oval:ssg-test_no_shosts_equiv:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_shosts_equiv_files_root:obj:1 of type file_object
BehaviorsPathFilename
no value/shosts.equiv
Remove User Host-Based Authentication Filesxccdf_org.ssgproject.content_rule_no_user_host_based_files highCCE-84056-1

Remove User Host-Based Authentication Files

Rule IDxccdf_org.ssgproject.content_rule_no_user_host_based_files
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-no_user_host_based_files:def:1
Time2025-11-18T17:07:14-05:00
Severityhigh
Identifiers:

CCE-84056-1

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010470
stigrefSV-230284r1017095_rule
Description
The ~/.shosts (in each user's home directory) files list remote hosts and users that are trusted by the local system. To remove these files, run the following command to delete them from any location:
$ sudo find / -name '.shosts' -type f -delete
Rationale
The .shosts files are used to configure host-based authentication for individual users or the system via SSH. Host-based authentication is not sufficient for preventing unauthorized access to the system, as it does not require interactive identification and authentication of a connection request, or for the use of two-factor authentication.
OVAL test results details

look for .shosts in /  oval:ssg-test_no_shosts:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_no_shosts_files_root:obj:1 of type file_object
BehaviorsPathFilename
no value/.shosts
Uninstall telnet-server Packagexccdf_org.ssgproject.content_rule_package_telnet-server_removed highCCE-82182-7

Uninstall telnet-server Package

Rule IDxccdf_org.ssgproject.content_rule_package_telnet-server_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_telnet-server_removed:def:1
Time2025-11-18T17:07:14-05:00
Severityhigh
Identifiers:

CCE-82182-7

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
pcidssReq-2.2.2
os-srgSRG-OS-000095-GPOS-00049
anssiR62
cis2.2.15
pcidss42.2.4, 2.2
stigidRHEL-08-040000
stigrefSV-230487r1017271_rule
Description
The telnet-server package can be removed with the following command:
$ sudo yum erase telnet-server
Rationale
It is detrimental for operating systems to provide, or install by default, functionality exceeding requirements or mission objectives. These unnecessary capabilities are often overlooked and therefore may remain unsecure. They increase the risk to the platform by providing additional attack vectors.
The telnet service provides an unencrypted remote access service which does not provide for the confidentiality and integrity of user passwords or the remote session. If a privileged user were to login using this service, the privileged user password could be compromised.
Removing the telnet-server package decreases the risk of the telnet service's accidental (or intentional) activation.
OVAL test results details

package telnet-server is removed  oval:ssg-test_package_telnet-server_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_telnet-server_removed:obj:1 of type rpminfo_object
Name
telnet-server
Uninstall tftp-server Packagexccdf_org.ssgproject.content_rule_package_tftp-server_removed highCCE-82436-7

Uninstall tftp-server Package

Rule IDxccdf_org.ssgproject.content_rule_package_tftp-server_removed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_tftp-server_removed:def:1
Time2025-11-18T17:07:14-05:00
Severityhigh
Identifiers:

CCE-82436-7

References:
cis-csc11, 12, 14, 15, 3, 8, 9
cobit5APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06
isa-62443-20094.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
anssiR62
cis2.2.16
pcidss42.2.4, 2.2
stigidRHEL-08-040190
stigrefSV-230533r1017295_rule
Description
The tftp-server package can be removed with the following command:
 $ sudo yum erase tftp-server
Rationale
Removing the tftp-server package decreases the risk of the accidental (or intentional) activation of tftp services.

If TFTP is required for operational support (such as transmission of router configurations), its use must be documented with the Information Systems Securty Manager (ISSM), restricted to only authorized personnel, and have access control rules established.
OVAL test results details

package tftp-server is removed  oval:ssg-test_package_tftp-server_removed:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_tftp-server_removed:obj:1 of type rpminfo_object
Name
tftp-server
Ensure tftp systemd Service Uses Secure Modexccdf_org.ssgproject.content_rule_tftp_uses_secure_mode_systemd mediumCCE-90667-7

Ensure tftp systemd Service Uses Secure Mode

Rule IDxccdf_org.ssgproject.content_rule_tftp_uses_secure_mode_systemd
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:14-05:00
Severitymedium
Identifiers:

CCE-90667-7

References:
nistIA-5 (1) (c)
os-srgSRG-OS-000074-GPOS-00042
stigidRHEL-08-040350
stigrefSV-230557r1088855_rule
Description
If running the Trivial File Transfer Protocol (TFTP) service is necessary, it should be configured to change its root directory at startup. To do so, find the path for the tftp systemd service:
$ sudo systemctl show tftp | grep ExecStart=
ExecStart={ path=/usr/sbin/in.tftpd ; argv[]=/usr/sbin/in.tftpd -s /var/lib/tftpboot ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }e
and ensure the ExecStart line on that file includes the -s option with a subdirectory:
ExecStart=/usr/sbin/in.tftpd -s /var/lib/tftpboot
Rationale
Using the -s option causes the TFTP service to only serve files from the given directory. Serving files from an intentionally-specified directory reduces the risk of sharing files which should remain private.
Enable the Hardware RNG Entropy Gatherer Servicexccdf_org.ssgproject.content_rule_service_rngd_enabled lowCCE-82831-9

Enable the Hardware RNG Entropy Gatherer Service

Rule IDxccdf_org.ssgproject.content_rule_service_rngd_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_rngd_enabled:def:1
Time2025-11-18T17:07:19-05:00
Severitylow
Identifiers:

CCE-82831-9

References:
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010471
stigrefSV-230285r1017096_rule
Description
The Hardware RNG Entropy Gatherer service should be enabled. The rngd service can be enabled with the following command:
$ sudo systemctl enable rngd.service
Rationale
The rngd service feeds random data from hardware device to kernel random device.
Warnings
warning  For RHEL versions 8.4 and above running with kernel FIPS mode enabled this rule is not applicable. The in-kernel deterministic random bit generator (DRBG) is used in FIPS mode instead. Consequently, the rngd service can't be started in FIPS mode.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.3"; printf "%s\n%s" "$real" "$expected" | sort -VC; } || ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.4"; printf "%s\n%s" "$expected" "$real" | sort -VC; } && ! ( [ "$(sysctl -a | grep -c 'fips_enabled.*1')" -eq 1 ] ) ) ); }; then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" unmask 'rngd.service'
if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
  "$SYSTEMCTL_EXEC" start 'rngd.service'
fi
"$SYSTEMCTL_EXEC" enable 'rngd.service'

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82831-9
  - DISA-STIG-RHEL-08-010471
  - enable_strategy
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - service_rngd_enabled

- name: Enable the Hardware RNG Entropy Gatherer Service - Enable service rngd
  block:

  - name: Gather the package facts
    ansible.builtin.package_facts:
      manager: auto

  - name: Enable the Hardware RNG Entropy Gatherer Service - Enable Service rngd
    ansible.builtin.systemd:
      name: rngd
      enabled: true
      state: started
      masked: false
    when:
    - '"rng-tools" in ansible_facts.packages'
  tags:
  - CCE-82831-9
  - DISA-STIG-RHEL-08-010471
  - enable_strategy
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - service_rngd_enabled
  - special_service_block
  when:
  - '"kernel" in ansible_facts.packages'
  - ( ansible_distribution == 'RedHat' and ansible_distribution_version is version('8.3',
    '<=') or ( ansible_distribution == 'RedHat' and ansible_distribution_version is
    version('8.4', '>=') ) )

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include enable_rngd

class enable_rngd {
  service {'rngd':
    enable => true,
    ensure => 'running',
  }
}


[customizations.services]
enabled = ["rngd"]

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

service enable rngd
OVAL test results details

package rng-tools is installed  oval:ssg-test_service_rngd_package_rng-tools_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_service_rngd_package_rng-tools_installed:obj:1 of type rpminfo_object
Name
rng-tools

Test that the rngd service is running  oval:ssg-test_service_running_rngd:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_service_running_rngd:obj:1 of type systemdunitproperty_object
UnitProperty
^rngd\.(socket|service)$ActiveState

systemd test  oval:ssg-test_multi_user_wants_rngd:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_rngd_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Verify the SSH Private Key Files Have a Passcodexccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected mediumCCE-90781-6

Verify the SSH Private Key Files Have a Passcode

Rule IDxccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-90781-6

References:
os-srgSRG-OS-000067-GPOS-00035
stigidRHEL-08-010100
stigrefSV-230230r1069287_rule
Description
When creating SSH key pairs, always use a passcode.
You can create such keys with the following command:
$ sudo ssh-keygen -n [passphrase]
Red Hat Enterprise Linux 8, for certificate-based authentication, must enforce authorized access to the corresponding private key.
Rationale
If an unauthorized user obtains access to a private key without a passcode, that user would have unauthorized access to any system where the associated public key has been installed.
Evaluation messages
info 
No candidate or applicable check found.
Set SSH Client Alive Count Maxxccdf_org.ssgproject.content_rule_sshd_set_keepalive mediumCCE-80907-9

Set SSH Client Alive Count Max

Rule IDxccdf_org.ssgproject.content_rule_sshd_set_keepalive
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_set_keepalive:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80907-9

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8
cjis5.5.6
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.1.11
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.3, CIP-007-3 R5.1, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistAC-2(5), AC-12, AC-17(a), SC-10, CM-6(a)
nist-csfDE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2
pcidssReq-8.1.8
os-srgSRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109
cis4.2.7
pcidss48.2.8, 8.2
stigidRHEL-08-010200
stigrefSV-230244r1069300_rule
Description
The SSH server sends at most ClientAliveCountMax messages during a SSH session and waits for a response from the SSH client. The option ClientAliveInterval configures timeout after each ClientAliveCountMax message. If the SSH server does not receive a response from the client, then the connection is considered unresponsive and terminated. For SSH earlier than v8.2, a ClientAliveCountMax value of 0 causes a timeout precisely when the ClientAliveInterval is set. Starting with v8.2, a value of 0 disables the timeout functionality completely. If the option is set to a number greater than 0, then the session will be disconnected after ClientAliveInterval * ClientAliveCountMax seconds without receiving a keep alive message.
Rationale
This ensures a user login will be terminated as soon as the ClientAliveInterval is reached.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

var_sshd_set_keepalive='1'


if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80907-9
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010200
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_set_keepalive
- name: XCCDF Value var_sshd_set_keepalive # promote to variable
  set_fact:
    var_sshd_set_keepalive: !!str 1
  tags:
    - always

- name: Set SSH Client Alive Count Max
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveCountMax\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveCountMax\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveCountMax\s+
      line: ClientAliveCountMax {{ var_sshd_set_keepalive }}
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80907-9
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010200
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_set_keepalive
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of ClientAliveCountMax setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_set_keepalive:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_set_keepalive:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)ClientAliveCountMax(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of ClientAliveCountMax is present  oval:ssg-test_ClientAliveCountMax_present_sshd_set_keepalive:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_set_keepalive:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_set_keepalive:obj:1
Set SSH Client Alive Intervalxccdf_org.ssgproject.content_rule_sshd_set_idle_timeout mediumCCE-80906-1

Set SSH Client Alive Interval

Rule IDxccdf_org.ssgproject.content_rule_sshd_set_idle_timeout
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_set_idle_timeout:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80906-1

References:
cis-csc1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8
cjis5.5.6
cobit5APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
cui3.1.11
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.3, CIP-007-3 R5.1, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistCM-6(a), AC-17(a), AC-2(5), AC-12, AC-17(a), SC-10, CM-6(a)
nist-csfDE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2
pcidssReq-8.1.8
os-srgSRG-OS-000126-GPOS-00066, SRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109, SRG-OS-000395-GPOS-00175
cis4.2.7
pcidss48.2.8, 8.2
stigidRHEL-08-010201
stigrefSV-244525r1017331_rule
Description
SSH allows administrators to set a network responsiveness timeout interval. After this interval has passed, the unresponsive client will be automatically logged out.

To set this timeout interval, edit the following line in /etc/ssh/sshd_config as follows:
ClientAliveInterval 600


The timeout interval is given in seconds. For example, have a timeout of 10 minutes, set interval to 600.

If a shorter timeout has already been set for the login shell, that value will preempt any SSH setting made in /etc/ssh/sshd_config. Keep in mind that some processes may stop SSH from correctly detecting that the user is idle.
Rationale
Terminating an idle ssh session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been let unattended.
Warnings
warning  SSH disconnecting unresponsive clients will not have desired effect without also configuring ClientAliveCountMax in the SSH service configuration.
warning  Following conditions may prevent the SSH session to time out:
  • Remote processes on the remote machine generates output. As the output has to be transferred over the network to the client, the timeout is reset every time such transfer happens.
  • Any scp or sftp activity by the same user to the host resets the timeout.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

sshd_idle_timeout_value='600'


if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80906-1
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010201
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_set_idle_timeout
- name: XCCDF Value sshd_idle_timeout_value # promote to variable
  set_fact:
    sshd_idle_timeout_value: !!str 600
  tags:
    - always

- name: Set SSH Client Alive Interval
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveInterval\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveInterval\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*ClientAliveInterval\s+
      line: ClientAliveInterval {{ sshd_idle_timeout_value }}
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80906-1
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010201
  - NIST-800-171-3.1.11
  - NIST-800-53-AC-12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-2(5)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-SC-10
  - PCI-DSS-Req-8.1.8
  - PCI-DSSv4-8.2
  - PCI-DSSv4-8.2.8
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_set_idle_timeout
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

timeout is configured  oval:ssg-test_sshd_idle_timeout:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_sshd_idle_timeout:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[\s]*(?i)ClientAliveInterval[\s]+(\d+)[\s]*(?:#.*)?$1

Verify that the value of ClientAliveInterval is present  oval:ssg-test_clientaliveinterval_present:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_set_idle_timeout:obj:1 of type textfilecontent54_object
Set
oval:ssg-object_sshd_idle_timeout:obj:1
Disable SSH Access via Empty Passwordsxccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords highCCE-80896-4

Disable SSH Access via Empty Passwords

Rule IDxccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_disable_empty_passwords:def:1
Time2025-11-18T17:07:24-05:00
Severityhigh
Identifiers:

CCE-80896-4

References:
cis-csc11, 12, 13, 14, 15, 16, 18, 3, 5, 9
cjis5.5.6
cobit5APO01.06, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06
cui3.1.1, 3.1.5
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, SR 7.6
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistAC-17(a), CM-7(a), CM-7(b), CM-6(a)
nist-csfPR.AC-4, PR.AC-6, PR.DS-5, PR.IP-1, PR.PT-3
osppFIA_UAU.1
pcidssReq-2.2.4
os-srgSRG-OS-000106-GPOS-00053, SRG-OS-000480-GPOS-00229, SRG-OS-000480-GPOS-00227
cis4.2.18
pcidss42.2.6, 2.2
stigidRHEL-08-020330
stigrefSV-230380r1069308_rule
Description
Disallow SSH login with empty passwords. The default SSH configuration disables logins with empty passwords. The appropriate configuration is used if no value is set for PermitEmptyPasswords.
To explicitly disallow SSH login from accounts with empty passwords, add or correct the following line in /etc/ssh/sshd_config:
PermitEmptyPasswords no
Any accounts with empty passwords should be disabled immediately, and PAM configuration should prevent users from being able to assign themselves empty passwords.
Rationale
Configuring this setting for the SSH daemon provides additional assurance that remote login via SSH will require a password, even in the event of misconfiguration elsewhere.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "PermitEmptyPasswords no" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80896-4
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-020330
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-2.2.4
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_empty_passwords

- name: Disable SSH Access via Empty Passwords
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitEmptyPasswords\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitEmptyPasswords\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitEmptyPasswords\s+
      line: PermitEmptyPasswords no
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80896-4
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-020330
  - NIST-800-171-3.1.1
  - NIST-800-171-3.1.5
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-2.2.4
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - high_severity
  - low_complexity
  - low_disruption
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_empty_passwords
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of PermitEmptyPasswords setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_disable_empty_passwords:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_disable_empty_passwords:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)PermitEmptyPasswords(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of PermitEmptyPasswords is present  oval:ssg-test_PermitEmptyPasswords_present_sshd_disable_empty_passwords:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_disable_empty_passwords:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_disable_empty_passwords:obj:1
Disable GSSAPI Authenticationxccdf_org.ssgproject.content_rule_sshd_disable_gssapi_auth mediumCCE-80897-2

Disable GSSAPI Authentication

Rule IDxccdf_org.ssgproject.content_rule_sshd_disable_gssapi_auth
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_disable_gssapi_auth:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80897-2

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.12
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
ism0418, 1055, 1402
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistCM-7(a), CM-7(b), CM-6(a), AC-17(a)
nist-csfPR.IP-1
osppFTP_ITC_EXT.1, FCS_SSH_EXT.1.2
os-srgSRG-OS-000364-GPOS-00151, SRG-OS-000480-GPOS-00227
stigidRHEL-08-010522
stigrefSV-244528r1017335_rule
Description
Unless needed, SSH should not permit extraneous or unnecessary authentication mechanisms like GSSAPI.
The default SSH configuration disallows authentications based on GSSAPI. The appropriate configuration is used if no value is set for GSSAPIAuthentication.
To explicitly disable GSSAPI authentication, add or correct the following line in /etc/ssh/sshd_config:
GSSAPIAuthentication no
Rationale
GSSAPI authentication is used to provide additional authentication mechanisms to applications. Allowing GSSAPI authentication through SSH exposes the system's GSSAPI to remote hosts, increasing the attack surface of the system.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "GSSAPIAuthentication no" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80897-2
  - DISA-STIG-RHEL-08-010522
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_gssapi_auth

- name: Disable GSSAPI Authentication
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*GSSAPIAuthentication\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*GSSAPIAuthentication\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*GSSAPIAuthentication\s+
      line: GSSAPIAuthentication no
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80897-2
  - DISA-STIG-RHEL-08-010522
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_gssapi_auth
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of GSSAPIAuthentication setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_disable_gssapi_auth:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/ssh/sshd_configGSSAPIAuthentication yes

Verify that the value of GSSAPIAuthentication is present  oval:ssg-test_GSSAPIAuthentication_present_sshd_disable_gssapi_auth:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/ssh/sshd_configGSSAPIAuthentication yes
Disable Kerberos Authenticationxccdf_org.ssgproject.content_rule_sshd_disable_kerb_auth mediumCCE-80898-0

Disable Kerberos Authentication

Rule IDxccdf_org.ssgproject.content_rule_sshd_disable_kerb_auth
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_disable_kerb_auth:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80898-0

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.12
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistAC-17(a), CM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1
osppFTP_ITC_EXT.1, FCS_SSH_EXT.1.2
os-srgSRG-OS-000364-GPOS-00151, SRG-OS-000480-GPOS-00227
stigidRHEL-08-010521
stigrefSV-230291r1069303_rule
Description
Unless needed, SSH should not permit extraneous or unnecessary authentication mechanisms like Kerberos.
The default SSH configuration disallows authentication validation through Kerberos. The appropriate configuration is used if no value is set for KerberosAuthentication.
To explicitly disable Kerberos authentication, add or correct the following line in /etc/ssh/sshd_config:
KerberosAuthentication no
Rationale
Kerberos authentication for SSH is often implemented using GSSAPI. If Kerberos is enabled through SSH, the SSH daemon provides a means of access to the system's Kerberos implementation. Configuring these settings for the SSH daemon provides additional assurance that remote logon via SSH will not use unused methods of authentication, even in the event of misconfiguration elsewhere.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "KerberosAuthentication no" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80898-0
  - DISA-STIG-RHEL-08-010521
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_kerb_auth

- name: Disable Kerberos Authentication
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*KerberosAuthentication\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*KerberosAuthentication\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*KerberosAuthentication\s+
      line: KerberosAuthentication no
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80898-0
  - DISA-STIG-RHEL-08-010521
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_kerb_auth
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of KerberosAuthentication setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_disable_kerb_auth:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_disable_kerb_auth:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)KerberosAuthentication(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of KerberosAuthentication is present  oval:ssg-test_KerberosAuthentication_present_sshd_disable_kerb_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_disable_kerb_auth:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_disable_kerb_auth:obj:1
Disable SSH Support for User Known Hostsxccdf_org.ssgproject.content_rule_sshd_disable_user_known_hosts mediumCCE-80902-0

Disable SSH Support for User Known Hosts

Rule IDxccdf_org.ssgproject.content_rule_sshd_disable_user_known_hosts
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_disable_user_known_hosts:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80902-0

References:
cis-csc11, 3, 9
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.12
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistAC-17(a), CM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010520
stigrefSV-230290r1069302_rule
Description
SSH can allow system users to connect to systems if a cache of the remote systems public keys is available. This should be disabled.

To ensure this behavior is disabled, add or correct the following line in /etc/ssh/sshd_config:
IgnoreUserKnownHosts yes
Rationale
Configuring this setting for the SSH daemon provides additional assurance that remote login via SSH will require a password, even in the event of misconfiguration elsewhere.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "IgnoreUserKnownHosts yes" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80902-0
  - DISA-STIG-RHEL-08-010520
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_user_known_hosts

- name: Disable SSH Support for User Known Hosts
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*IgnoreUserKnownHosts\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*IgnoreUserKnownHosts\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*IgnoreUserKnownHosts\s+
      line: IgnoreUserKnownHosts yes
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80902-0
  - DISA-STIG-RHEL-08-010520
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_user_known_hosts
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of IgnoreUserKnownHosts setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_disable_user_known_hosts:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_disable_user_known_hosts:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)IgnoreUserKnownHosts(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of IgnoreUserKnownHosts is present  oval:ssg-test_IgnoreUserKnownHosts_present_sshd_disable_user_known_hosts:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_disable_user_known_hosts:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_disable_user_known_hosts:obj:1
Disable X11 Forwardingxccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding mediumCCE-83360-8

Disable X11 Forwarding

Rule IDxccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_disable_x11_forwarding:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-83360-8

References:
nistCM-6(b)
os-srgSRG-OS-000480-GPOS-00227
pcidss42.2.6, 2.2
stigidRHEL-08-040340
stigrefSV-230555r1017317_rule
Description
The X11Forwarding parameter provides the ability to tunnel X11 traffic through the connection to enable remote graphic connections. SSH has the capability to encrypt remote X11 connections when SSH's X11Forwarding option is enabled.
The default SSH configuration disables X11Forwarding. The appropriate configuration is used if no value is set for X11Forwarding.
To explicitly disable X11 Forwarding, add or correct the following line in /etc/ssh/sshd_config:
X11Forwarding no
Rationale
Disable X11 forwarding unless there is an operational requirement to use X11 applications directly. There is a small risk that the remote X11 servers of users who are logged in via SSH with X11 forwarding could be compromised by other users on the X11 server. Note that even if X11 forwarding is disabled, users can always install their own forwarders.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "X11Forwarding no" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83360-8
  - DISA-STIG-RHEL-08-040340
  - NIST-800-53-CM-6(b)
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_x11_forwarding

- name: Disable X11 Forwarding
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11Forwarding\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11Forwarding\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11Forwarding\s+
      line: X11Forwarding no
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-83360-8
  - DISA-STIG-RHEL-08-040340
  - NIST-800-53-CM-6(b)
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_disable_x11_forwarding
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of X11Forwarding setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_disable_x11_forwarding:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/ssh/sshd_configX11Forwarding yes

Verify that the value of X11Forwarding is present  oval:ssg-test_X11Forwarding_present_sshd_disable_x11_forwarding:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/ssh/sshd_configX11Forwarding yes
Do Not Allow SSH Environment Optionsxccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env mediumCCE-80903-8

Do Not Allow SSH Environment Options

Rule IDxccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_do_not_permit_user_env:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80903-8

References:
cis-csc11, 3, 9
cjis5.5.6
cobit5BAI10.01, BAI10.02, BAI10.03, BAI10.05
cui3.1.12
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.4.3.2, 4.3.4.3.3
isa-62443-2013SR 7.6
iso27001-2013A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4
nistAC-17(a), CM-7(a), CM-7(b), CM-6(a)
nist-csfPR.IP-1
pcidssReq-2.2.4
os-srgSRG-OS-000480-GPOS-00229
cis4.2.20
pcidss42.2.6, 2.2
stigidRHEL-08-010830
stigrefSV-230330r1069305_rule
Description
Ensure that users are not able to override environment variables of the SSH daemon.
The default SSH configuration disables environment processing. The appropriate configuration is used if no value is set for PermitUserEnvironment.
To explicitly disable Environment options, add or correct the following /etc/ssh/sshd_config:
PermitUserEnvironment no
Rationale
SSH environment options potentially allow users to bypass access restriction in some configurations.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "PermitUserEnvironment no" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80903-8
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010830
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-2.2.4
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_do_not_permit_user_env

- name: Do Not Allow SSH Environment Options
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitUserEnvironment\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitUserEnvironment\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PermitUserEnvironment\s+
      line: PermitUserEnvironment no
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80903-8
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010830
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - PCI-DSS-Req-2.2.4
  - PCI-DSSv4-2.2
  - PCI-DSSv4-2.2.6
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_do_not_permit_user_env
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of PermitUserEnvironment setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_do_not_permit_user_env:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_do_not_permit_user_env:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)PermitUserEnvironment(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of PermitUserEnvironment is present  oval:ssg-test_PermitUserEnvironment_present_sshd_do_not_permit_user_env:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_do_not_permit_user_env:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_do_not_permit_user_env:obj:1
Enable Use of Strict Mode Checkingxccdf_org.ssgproject.content_rule_sshd_enable_strictmodes mediumCCE-80904-6

Enable Use of Strict Mode Checking

Rule IDxccdf_org.ssgproject.content_rule_sshd_enable_strictmodes
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_enable_strictmodes:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80904-6

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.1.12
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-6, AC-17(a), CM-6(a)
nist-csfPR.AC-4, PR.DS-5
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-010500
stigrefSV-230288r1069301_rule
Description
SSHs StrictModes option checks file and ownership permissions in the user's home directory .ssh folder before accepting login. If world- writable permissions are found, logon is rejected.
The default SSH configuration has StrictModes enabled. The appropriate configuration is used if no value is set for StrictModes.
To explicitly enable StrictModes in SSH, add or correct the following line in /etc/ssh/sshd_config:
StrictModes yes
Rationale
If other users have access to modify user-specific SSH configuration files, they may be able to log into the system as another user.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "StrictModes yes" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80904-6
  - DISA-STIG-RHEL-08-010500
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-6
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_strictmodes

- name: Enable Use of Strict Mode Checking
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*StrictModes\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*StrictModes\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*StrictModes\s+
      line: StrictModes yes
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80904-6
  - DISA-STIG-RHEL-08-010500
  - NIST-800-171-3.1.12
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-6
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_strictmodes
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of StrictModes setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_enable_strictmodes:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_enable_strictmodes:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)StrictModes(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of StrictModes is present  oval:ssg-test_StrictModes_present_sshd_enable_strictmodes:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_enable_strictmodes:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_enable_strictmodes:obj:1
Enable SSH Warning Bannerxccdf_org.ssgproject.content_rule_sshd_enable_warning_banner mediumCCE-80905-3

Enable SSH Warning Banner

Rule IDxccdf_org.ssgproject.content_rule_sshd_enable_warning_banner
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_enable_warning_banner:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80905-3

References:
cis-csc1, 12, 15, 16
cjis5.5.6
cobit5DSS05.04, DSS05.10, DSS06.10
cui3.1.9
hipaa164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii)
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-8(a), AC-8(c), AC-17(a), CM-6(a)
nist-csfPR.AC-7
osppFTA_TAB.1
pcidssReq-2.2.4
os-srgSRG-OS-000023-GPOS-00006, SRG-OS-000228-GPOS-00088
stigidRHEL-08-010040
stigrefSV-230225r1069297_rule
Description
To enable the warning banner and ensure it is consistent across the system, add or correct the following line in /etc/ssh/sshd_config:
Banner /etc/issue
Another section contains information on how to create an appropriate system-wide warning banner.
Rationale
The warning message reinforces policy awareness during the logon process and facilitates possible legal action against attackers. Alternatively, systems whose ownership should not be obvious should ensure usage of a banner that does not provide easy attribution.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "Banner /etc/issue" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80905-3
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010040
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-2.2.4
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner

- name: Enable SSH Warning Banner
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*Banner\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*Banner\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*Banner\s+
      line: Banner /etc/issue
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80905-3
  - CJIS-5.5.6
  - DISA-STIG-RHEL-08-010040
  - NIST-800-171-3.1.9
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-2.2.4
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of Banner setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_enable_warning_banner:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_enable_warning_banner:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)Banner(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of Banner is present  oval:ssg-test_Banner_present_sshd_enable_warning_banner:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_enable_warning_banner:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_enable_warning_banner:obj:1
Enable SSH Print Last Logxccdf_org.ssgproject.content_rule_sshd_print_last_log mediumCCE-82281-7

Enable SSH Print Last Log

Rule IDxccdf_org.ssgproject.content_rule_sshd_print_last_log
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_print_last_log:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82281-7

References:
cis-csc1, 12, 15, 16
cobit5DSS05.04, DSS05.10, DSS06.10
isa-62443-20094.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9
iso27001-2013A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3
nistAC-9, AC-9(1)
nist-csfPR.AC-7
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-020350
stigrefSV-230382r1069309_rule
Description
Ensure that SSH will display the date and time of the last successful account logon.
The default SSH configuration enables print of the date and time of the last login. The appropriate configuration is used if no value is set for PrintLastLog.
To explicitly enable LastLog in SSH, add or correct the following line in /etc/ssh/sshd_config:
PrintLastLog yes
Rationale
Providing users feedback on when account accesses last occurred facilitates user recognition and reporting of unauthorized account use.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "PrintLastLog yes" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82281-7
  - DISA-STIG-RHEL-08-020350
  - NIST-800-53-AC-9
  - NIST-800-53-AC-9(1)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_print_last_log

- name: Enable SSH Print Last Log
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PrintLastLog\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PrintLastLog\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*PrintLastLog\s+
      line: PrintLastLog yes
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82281-7
  - DISA-STIG-RHEL-08-020350
  - NIST-800-53-AC-9
  - NIST-800-53-AC-9(1)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_print_last_log
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of PrintLastLog setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_print_last_log:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_print_last_log:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)PrintLastLog(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of PrintLastLog is present  oval:ssg-test_PrintLastLog_present_sshd_print_last_log:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_print_last_log:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_print_last_log:obj:1
Force frequent session key renegotiationxccdf_org.ssgproject.content_rule_sshd_rekey_limit mediumCCE-82177-7

Force frequent session key renegotiation

Rule IDxccdf_org.ssgproject.content_rule_sshd_rekey_limit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_rekey_limit:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82177-7

References:
osppFCS_SSH_EXT.1.8
os-srgSRG-OS-000480-GPOS-00227, SRG-OS-000033-GPOS-00014
stigidRHEL-08-040161
stigrefSV-230527r1017288_rule
Description
The RekeyLimit parameter specifies how often the session key of the is renegotiated, both in terms of amount of data that may be transmitted and the time elapsed.
To decrease the default limits, add or correct the following line in /etc/ssh/sshd_config:
RekeyLimit 1G 1h
Rationale
By decreasing the limit based on the amount of data and enabling time-based limit, effects of potential attacks against encryption keys are limited.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

var_rekey_limit_size='1G'
var_rekey_limit_time='1h'



if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*RekeyLimit\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "RekeyLimit $var_rekey_limit_size $var_rekey_limit_time" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82177-7
  - DISA-STIG-RHEL-08-040161
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - sshd_rekey_limit
- name: XCCDF Value var_rekey_limit_size # promote to variable
  set_fact:
    var_rekey_limit_size: !!str 1G
  tags:
    - always
- name: XCCDF Value var_rekey_limit_time # promote to variable
  set_fact:
    var_rekey_limit_time: !!str 1h
  tags:
    - always

- name: Force frequent session key renegotiation
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*RekeyLimit\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*RekeyLimit\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*RekeyLimit\s+
      line: RekeyLimit {{ var_rekey_limit_size }} {{ var_rekey_limit_time }}
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82177-7
  - DISA-STIG-RHEL-08-040161
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - sshd_rekey_limit
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of RekeyLimit setting in the file  oval:ssg-test_sshd_rekey_limit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_rekey_limit:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[\s]*RekeyLimit[\s]+(.*)$1
Use Only FIPS 140-2 Validated Key Exchange Algorithmsxccdf_org.ssgproject.content_rule_sshd_use_approved_kex_ordered_stig mediumCCE-86059-3

Use Only FIPS 140-2 Validated Key Exchange Algorithms

Rule IDxccdf_org.ssgproject.content_rule_sshd_use_approved_kex_ordered_stig
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_use_approved_kex_ordered_stig:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-86059-3

References:
nistAC-17(2)
os-srgSRG-OS-000250-GPOS-00093
stigidRHEL-08-040342
stigrefSV-255924r1017372_rule
Description
Limit the key exchange algorithms to those which are FIPS-approved. Add or modify the following line in /etc/crypto-policies/back-ends/opensshserver.config
CRYPTO_POLICY='-oKexAlgorithms=ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512'
This rule ensures that only the key exchange algorithms mentioned above (or their subset) are configured for use, keeping the given order of algorithms.
Rationale
FIPS-approved key exchange algorithms are required to be used. The system will attempt to use the first algorithm presented by the client that matches the server list. Listing the values "strongest to weakest" is a method to ensure the use of the strongest algorithm available to secure the SSH connection.
Warnings
warning  The system needs to be rebooted for these changes to take effect.
warning  This rule doesn't come with a remediation, automatically changing the crypto-policies may be too disruptive.
warning  System crypto modules must be provided by a vendor that undergoes FIPS-140 certifications. FIPS-140 is applicable to all Federal agencies that use cryptographic-based security systems to protect sensitive information in computer and telecommunication systems (including voice systems) as defined in Section 5131 of the Information Technology Management Reform Act of 1996, Public Law 104-106. This standard shall be used in designing and implementing cryptographic modules that Federal departments and agencies operate or are operated for them under contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf To meet this requirements, the system has to have cryptographic software provided by a vendor that has undergone this certification. This means providing documentation, test results, design information, and independent third party review by an accredited lab. While open source software is capable of meeting this, it does not meet FIPS-140 unless the vendor submits to this process.
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of Kex algorithms setting in the /etc/crypto-policies/back-ends/opensshserver.config file  oval:ssg-test_sshd_use_approved_kex_ordered_stig:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/crypto-policies/back-ends/opensshserver.configCRYPTO_POLICY='-oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc -oMACs=hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512 -oGSSAPIKexAlgorithms=gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-,gss-gex-sha1-,gss-group14-sha1- -oKexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1 -oHostKeyAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oPubkeyAcceptedKeyTypes=ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,ssh-rsa,ssh-rsa-cert-v01@openssh.com -oCASignatureAlgorithms=ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa'
SSH server uses strong entropy to seedxccdf_org.ssgproject.content_rule_sshd_use_strong_rng lowCCE-82462-3

SSH server uses strong entropy to seed

Rule IDxccdf_org.ssgproject.content_rule_sshd_use_strong_rng
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_use_strong_rng:def:1
Time2025-11-18T17:07:24-05:00
Severitylow
Identifiers:

CCE-82462-3

References:
os-srgSRG-OS-000480-GPOS-00232, SRG-OS-000480-GPOS-00227
stigidRHEL-08-010292
stigrefSV-230253r1044799_rule
Description
To set up SSH server to use entropy from a high-quality source, edit the /etc/sysconfig/sshd file. The SSH_USE_STRONG_RNG configuration value determines how many bytes of entropy to use, so make sure that the file contains line
SSH_USE_STRONG_RNG=32
Rationale
SSH implementation in Red Hat Enterprise Linux 8 uses the openssl library, which doesn't use high-entropy sources by default. Randomness is needed to generate data-encryption keys, and as plaintext padding and initialization vectors in encryption algorithms, and high-quality entropy elliminates the possibility that the output of the random number generator used by SSH would be known to potential attackers.
Warnings
warning  This setting can cause problems on computers without the hardware random generator, because insufficient entropy causes the connection to be blocked until enough entropy is available.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/sysconfig/sshd" ] ; then
    
    LC_ALL=C sed -i "/^\s*SSH_USE_STRONG_RNG\s*=\s*/d" "/etc/sysconfig/sshd"
else
    touch "/etc/sysconfig/sshd"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/sysconfig/sshd"

cp "/etc/sysconfig/sshd" "/etc/sysconfig/sshd.bak"
# Insert before the line matching the regex '^#\s*SSH_USE_STRONG_RNG'.
line_number="$(LC_ALL=C grep -n "^#\s*SSH_USE_STRONG_RNG" "/etc/sysconfig/sshd.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
    # There was no match of '^#\s*SSH_USE_STRONG_RNG', insert at
    # the end of the file.
    printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd"
else
    head -n "$(( line_number - 1 ))" "/etc/sysconfig/sshd.bak" > "/etc/sysconfig/sshd"
    printf '%s\n' "SSH_USE_STRONG_RNG=32" >> "/etc/sysconfig/sshd"
    tail -n "+$(( line_number ))" "/etc/sysconfig/sshd.bak" >> "/etc/sysconfig/sshd"
fi
# Clean up after ourselves.
rm "/etc/sysconfig/sshd.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82462-3
  - DISA-STIG-RHEL-08-010292
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_use_strong_rng

- name: Setting unquoted shell-style assignment of 'SSH_USE_STRONG_RNG' to '32' in
    '/etc/sysconfig/sshd'
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/sysconfig/sshd
      create: true
      regexp: (?i)^\s*SSH_USE_STRONG_RNG=
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/sysconfig/sshd
    ansible.builtin.lineinfile:
      path: /etc/sysconfig/sshd
      create: true
      regexp: (?i)^\s*SSH_USE_STRONG_RNG=
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/sysconfig/sshd
    ansible.builtin.lineinfile:
      path: /etc/sysconfig/sshd
      create: true
      regexp: (?i)^\s*SSH_USE_STRONG_RNG=
      line: SSH_USE_STRONG_RNG=32
      state: present
      insertbefore: ^# SSH_USE_STRONG_RNG
      validate: /usr/bin/bash -n %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82462-3
  - DISA-STIG-RHEL-08-010292
  - low_complexity
  - low_disruption
  - low_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_use_strong_rng
OVAL test results details

tests the value of SSH_USE_STRONG_RNG setting in the /etc/sysconfig/sshd file  oval:ssg-test_sshd_use_strong_rng:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/sysconfig/sshdSSH_USE_STRONG_RNG=0
Prevent remote hosts from connecting to the proxy displayxccdf_org.ssgproject.content_rule_sshd_x11_use_localhost mediumCCE-84058-7

Prevent remote hosts from connecting to the proxy display

Rule IDxccdf_org.ssgproject.content_rule_sshd_x11_use_localhost
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sshd_x11_use_localhost:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-84058-7

References:
nistCM-6(b)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040341
stigrefSV-230556r1017318_rule
Description
The SSH daemon should prevent remote hosts from connecting to the proxy display.
The default SSH configuration for X11UseLocalhost is yes, which prevents remote hosts from connecting to the proxy display.
To explicitly prevent remote connections to the proxy display, add or correct the following line in /etc/ssh/sshd_config: X11UseLocalhost yes
Rationale
When X11 forwarding is enabled, there may be additional exposure to the server and client displays if the sshd proxy display is configured to listen on the wildcard address. By default, sshd binds the forwarding server to the loopback address and sets the hostname part of the DISPLAY environment variable to localhost. This prevents remote hosts from connecting to the proxy display.

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

if [ -e "/etc/ssh/sshd_config" ] ; then
    
    LC_ALL=C sed -i "/^\s*X11UseLocalhost\s\+/Id" "/etc/ssh/sshd_config"
else
    touch "/etc/ssh/sshd_config"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/ssh/sshd_config"

cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert at the beginning of the file
printf '%s\n' "X11UseLocalhost yes" > "/etc/ssh/sshd_config"
cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84058-7
  - DISA-STIG-RHEL-08-040341
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_x11_use_localhost

- name: Prevent remote hosts from connecting to the proxy display
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11UseLocalhost\s+
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11UseLocalhost\s+
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      create: true
      regexp: (?i)(?i)^\s*X11UseLocalhost\s+
      line: X11UseLocalhost yes
      state: present
      insertbefore: BOF
      validate: /usr/sbin/sshd -t -f %s
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-84058-7
  - DISA-STIG-RHEL-08-040341
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_x11_use_localhost
OVAL test results details

Verify if Profile set Value sshd_required as not required  oval:ssg-test_sshd_not_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is removed  oval:ssg-test_package_openssh-server_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Verify if Profile set Value sshd_required as required  oval:ssg-test_sshd_required:tst:1  false

Following items have been found on the system:
Result of item-state comparisonVar refValue
falseoval:ssg-sshd_required:var:10

Verify if Value of sshd_required is the default  oval:ssg-test_sshd_requirement_unset:tst:1  true

Following items have been found on the system:
Result of item-state comparisonVar refValue
trueoval:ssg-sshd_required:var:10

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

tests the value of X11UseLocalhost setting in the /etc/ssh/sshd_config file  oval:ssg-test_sshd_x11_use_localhost:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sshd_x11_use_localhost:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/ssh/sshd_config^[ \t]*(?i)X11UseLocalhost(?-i)[ \t]+(.+?)[ \t]*(?:$|#)1

Verify that the value of X11UseLocalhost is present  oval:ssg-test_X11UseLocalhost_present_sshd_x11_use_localhost:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_collection_obj_sshd_x11_use_localhost:obj:1 of type textfilecontent54_object
Set
oval:ssg-obj_sshd_x11_use_localhost:obj:1
Install the OpenSSH Server Packagexccdf_org.ssgproject.content_rule_package_openssh-server_installed mediumCCE-83303-8

Install the OpenSSH Server Package

Rule IDxccdf_org.ssgproject.content_rule_package_openssh-server_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_openssh-server_installed:def:1
Time2025-11-18T17:07:19-05:00
Severitymedium
Identifiers:

CCE-83303-8

References:
cis-csc13, 14
cobit5APO01.06, DSS05.02, DSS05.04, DSS05.07, DSS06.02, DSS06.06
isa-62443-2013SR 3.1, SR 3.8, SR 4.1, SR 4.2, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a)
nist-csfPR.DS-2, PR.DS-5
osppFIA_UAU.5, FTP_ITC_EXT.1, FCS_SSH_EXT.1, FCS_SSHS_EXT.1
os-srgSRG-OS-000423-GPOS-00187, SRG-OS-000424-GPOS-00188, SRG-OS-000425-GPOS-00189, SRG-OS-000426-GPOS-00190
stigidRHEL-08-040159
stigrefSV-244549r958908_rule
Description
The openssh-server package should be installed. The openssh-server package can be installed with the following command:
$ sudo yum install openssh-server
Rationale
Without protection of the transmitted information, confidentiality, and integrity may be compromised because unprotected communications can be intercepted and either read or altered.
OVAL test results details

package openssh-server is installed  oval:ssg-test_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64
Enable the OpenSSH Servicexccdf_org.ssgproject.content_rule_service_sshd_enabled mediumCCE-82426-8

Enable the OpenSSH Service

Rule IDxccdf_org.ssgproject.content_rule_service_sshd_enabled
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-service_sshd_enabled:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82426-8

References:
cis-csc13, 14
cobit5APO01.06, DSS05.02, DSS05.04, DSS05.07, DSS06.02, DSS06.06
cui3.1.13, 3.5.4, 3.13.8
isa-62443-2013SR 3.1, SR 3.8, SR 4.1, SR 4.2, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), SC-8, SC-8(1), SC-8(2), SC-8(3), SC-8(4)
nist-csfPR.DS-2, PR.DS-5
os-srgSRG-OS-000423-GPOS-00187, SRG-OS-000424-GPOS-00188, SRG-OS-000425-GPOS-00189, SRG-OS-000426-GPOS-00190
stigidRHEL-08-040160
stigrefSV-230526r958908_rule
Description
The SSH server service, sshd, is commonly needed. The sshd service can be enabled with the following command:
$ sudo systemctl enable sshd.service
Rationale
Without protection of the transmitted information, confidentiality, and integrity may be compromised because unprotected communications can be intercepted and either read or altered.

This checklist item applies to both internal and external networks and all types of information system components from which information can be transmitted (e.g., servers, mobile devices, notebook computers, printers, copiers, scanners, etc). Communication paths outside the physical protection of a controlled boundary are exposed to the possibility of interception and modification.
OVAL test results details

package openssh-server is installed  oval:ssg-test_service_sshd_package_openssh-server_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedopenssh-serverx86_64(none)24.el88.0p10:8.0p1-24.el8199e2f91fd431d51openssh-server-0:8.0p1-24.el8.x86_64

Test that the sshd service is running  oval:ssg-test_service_running_sshd:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
truesshd.serviceActiveStateactive
falsesshd.socketActiveStateinactive

systemd test  oval:ssg-test_multi_user_wants_sshd:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
truemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_sshd_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Verify Permissions on SSH Server Private *_key Key Filesxccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key mediumCCE-82424-3

Verify Permissions on SSH Server Private *_key Key Files

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_sshd_private_key:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82424-3

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.1.13, 3.13.10
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-17(a), CM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
pcidssReq-2.2.4
os-srgSRG-OS-000480-GPOS-00227
anssiR50
cis4.2.2
pcidss42.2.6, 2.2
stigidRHEL-08-010490
stigrefSV-230287r1017098_rule
Description
SSH server private keys - files that match the /etc/ssh/*_key glob, have to have restricted permissions. If those files are owned by the root user and the root group, they have to have the 0600 permission or stricter. If they are owned by the root user, but by a dedicated group ssh_keys, they can have the 0640 permission or stricter.
Rationale
If an unauthorized user obtains the private SSH host key file, the host could be impersonated.
Warnings
warning  Remediation is not possible at bootable container build time because SSH host keys are generated post-deployment.
OVAL test results details

No keys that have unsafe ownership/permissions combination exist  oval:ssg-test_no_offending_keys:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_offending_keys:obj:1 of type file_object
PathFilenameFilterFilterFilter
/etc/ssh.*_key$oval:ssg-exclude_symlinks__sshd_private_key:ste:1oval:ssg-filter_ssh_key_owner_root:ste:1oval:ssg-filter_ssh_key_owner_ssh_keys:ste:1
Verify Permissions on SSH Server Public *.pub Key Filesxccdf_org.ssgproject.content_rule_file_permissions_sshd_pub_key mediumCCE-82428-4

Verify Permissions on SSH Server Public *.pub Key Files

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_sshd_pub_key
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_sshd_pub_key:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82428-4

References:
cis-csc12, 13, 14, 15, 16, 18, 3, 5
cobit5APO01.06, DSS05.04, DSS05.07, DSS06.02
cui3.1.13, 3.13.10
isa-62443-20094.3.3.7.3
isa-62443-2013SR 2.1, SR 5.2
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistAC-17(a), CM-6(a), AC-6(1)
nist-csfPR.AC-4, PR.DS-5
pcidssReq-2.2.4
os-srgSRG-OS-000480-GPOS-00227
anssiR50
cis4.2.3
pcidss42.2.6, 2.2
stigidRHEL-08-010480
stigrefSV-230286r1017097_rule
Description
To properly set the permissions of /etc/ssh/*.pub, run the command:
$ sudo chmod 0644 /etc/ssh/*.pub
Rationale
If a public host key file is modified by an unauthorized user, the SSH service may be compromised.
Warnings
warning  Remediation is not possible at bootable container build time because SSH host keys are generated post-deployment.
OVAL test results details

Testing mode of /etc/ssh/  oval:ssg-test_file_permissions_sshd_pub_key_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_sshd_pub_key_0:obj:1 of type file_object
PathFilenameFilterFilter
/etc/ssh^.*\.pub$oval:ssg-exclude_symlinks__sshd_pub_key:ste:1oval:ssg-state_file_permissions_sshd_pub_key_0_mode_0644or_stricter_:ste:1
Certificate status checking in SSSDxccdf_org.ssgproject.content_rule_sssd_certificate_verification mediumCCE-86120-3

Certificate status checking in SSSD

Rule IDxccdf_org.ssgproject.content_rule_sssd_certificate_verification
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sssd_certificate_verification:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-86120-3

References:
nistIA-2(11)
os-srgSRG-OS-000375-GPOS-00160, SRG-OS-000377-GPOS-00162
stigidRHEL-08-010400
stigrefSV-230274r1017089_rule
Description
Multifactor solutions that require devices separate from information systems gaining access include, for example, hardware tokens providing time-based or challenge-response authenticators and smart cards. Configuring certificate_verification to ocsp_dgst=sha1 ensures that certificates for multifactor solutions are checked via Online Certificate Status Protocol (OCSP).
Rationale
Ensuring that multifactor solutions certificates are checked via Online Certificate Status Protocol (OCSP) ensures the security of the system.

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q sssd-common; then

var_sssd_certificate_verification_digest_function='sha1'


# sssd configuration files must be created with 600 permissions if they don't exist
# otherwise the sssd module fails to start
OLD_UMASK=$(umask)
umask u=rw,go=

MAIN_CONF="/etc/sssd/conf.d/certificate_verification.conf"

found=false

# set value in all files if they contain section or key
for f in $(echo -n "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[sssd\]([^\n\[]*\n+)+?[[:space:]]*certificate_verification" "$f"; then

            sed -i "s/certificate_verification[^(\n)]*/certificate_verification=ocsp_dgst=$var_sssd_certificate_verification_digest_function/" "$f"

            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[sssd\]" "$f"; then

            sed -i "/[[:space:]]*\[sssd\]/a certificate_verification=ocsp_dgst=$var_sssd_certificate_verification_digest_function" "$f"

            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "$MAIN_CONF /etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"

    echo -e "[sssd]\ncertificate_verification=ocsp_dgst=$var_sssd_certificate_verification_digest_function" >> "$file"

fi

umask $OLD_UMASK

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86120-3
  - DISA-STIG-RHEL-08-010400
  - NIST-800-53-IA-2(11)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_certificate_verification
- name: XCCDF Value var_sssd_certificate_verification_digest_function # promote to variable
  set_fact:
    var_sssd_certificate_verification_digest_function: !!str sha1
  tags:
    - always

- name: Ensure that "certificate_verification" is not set in /etc/sssd/sssd.conf
  community.general.ini_file:
    path: /etc/sssd/sssd.conf
    section: sssd
    option: certificate_verification
    state: absent
    mode: 384
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-86120-3
  - DISA-STIG-RHEL-08-010400
  - NIST-800-53-IA-2(11)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_certificate_verification

- name: Ensure that "certificate_verification" is not set in  /etc/sssd/conf.d/*.conf
  community.general.ini_file:
    path: /etc/sssd/conf.d/*.conf
    section: sssd
    option: certificate_verification
    state: absent
    mode: 384
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-86120-3
  - DISA-STIG-RHEL-08-010400
  - NIST-800-53-IA-2(11)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_certificate_verification

- name: Ensure that "certificate_verification" is set
  community.general.ini_file:
    path: /etc/sssd/conf.d/certificate_verification.conf
    section: sssd
    option: certificate_verification
    value: ocsp_dgst={{ var_sssd_certificate_verification_digest_function }}
    state: present
    mode: 384
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-86120-3
  - DISA-STIG-RHEL-08-010400
  - NIST-800-53-IA-2(11)
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_certificate_verification
OVAL test results details

test the value of certificate_verification in sssd configuration  oval:ssg-test_sssd_certificate_verification:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_certificate_verification:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/sssd/(sssd|conf\.d/.*)\.conf$^[\s]*\[sssd](?:[^\n\[]*\n+)+?[\s]*certificate_verification\s*=\s*ocsp_dgst=(\w+)$1
Enable Certmap in SSSDxccdf_org.ssgproject.content_rule_sssd_enable_certmap mediumCCE-86060-1

Enable Certmap in SSSD

Rule IDxccdf_org.ssgproject.content_rule_sssd_enable_certmap
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sssd_enable_certmap:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-86060-1

References:
nistIA-5 (2) (c)
os-srgSRG-OS-000068-GPOS-00036
stigidRHEL-08-020090
stigrefSV-230355r1017168_rule
Description
SSSD should be configured to verify the certificate of the user or group. To set this up ensure that section like certmap/testing.test/rule_name is setup in /etc/sssd/sssd.conf. For example
[certmap/testing.test/rule_name]
matchrule =<SAN>.*EDIPI@mil
maprule = (userCertificate;binary={cert!bin})
domains = testing.test
Rationale
Without mapping the certificate used to authenticate to the user account, the ability to determine the identity of the individual user or group will not be available for forensic analysis.
Warnings
warning  Automatic remediation of this control is not available, since all of the settings in in the certmap need to be customized.
OVAL test results details

tests the presence of '\[certmap\/.+\/.+\]' setting in the /etc/sssd/sssd.conf file  oval:ssg-test_sssd_enable_certmap:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_enable_certmap:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sssd/sssd.conf^[\s]*\[certmap\/.+\/.+\][\s]*$1
Enable Smartcards in SSSDxccdf_org.ssgproject.content_rule_sssd_enable_smartcards mediumCCE-80909-5

Enable Smartcards in SSSD

Rule IDxccdf_org.ssgproject.content_rule_sssd_enable_smartcards
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-sssd_enable_smartcards:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-80909-5

References:
ism0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561
pcidssReq-8.3
os-srgSRG-OS-000375-GPOS-00160, SRG-OS-000105-GPOS-00052, SRG-OS-000106-GPOS-00053, SRG-OS-000107-GPOS-00054, SRG-OS-000108-GPOS-00055
stigidRHEL-08-020250
stigrefSV-230372r1017184_rule
Description
SSSD should be configured to authenticate access to the system using smart cards. To enable smart cards in SSSD, set pam_cert_auth to True under the [pam] section in /etc/sssd/sssd.conf. For example:
[pam]
pam_cert_auth = True
Add or update "pam_sss.so" line in auth section of "/etc/pam.d/system-auth" file to include "try_cert_auth" or "require_cert_auth" option, like in the following example:
/etc/pam.d/system-auth:auth [success=done authinfo_unavail=ignore ignore=ignore default=die] pam_sss.so try_cert_auth
Also add or update "pam_sss.so" line in auth section of "/etc/pam.d/smartcard-auth" file to include the "allow_missing_name" option, like in the following example:
/etc/pam.d/smartcard-auth:auth sufficient pam_sss.so allow_missing_name
Rationale
Using an authentication device, such as a CAC or token that is separate from the information system, ensures that even if the information system is compromised, that compromise will not affect credentials stored on the authentication device.

Multi-Factor Authentication (MFA) solutions that require devices separate from information systems gaining access include, for example, hardware tokens providing time-based or challenge-response authenticators and smart cards or similar secure authentication devices issued by an organization or identity provider.

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if rpm --quiet -q sssd-common; then

# sssd configuration files must be created with 600 permissions if they don't exist
# otherwise the sssd module fails to start
OLD_UMASK=$(umask)
umask u=rw,go=

found=false

# set value in all files if they contain section or key
for f in $(echo -n "/etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf"); do
    if [ ! -e "$f" ]; then
        continue
    fi

    # find key in section and change value
    if grep -qzosP "[[:space:]]*\[pam\]([^\n\[]*\n+)+?[[:space:]]*pam_cert_auth" "$f"; then

            sed -i "s/pam_cert_auth[^(\n)]*/pam_cert_auth=True/" "$f"

            found=true

    # find section and add key = value to it
    elif grep -qs "[[:space:]]*\[pam\]" "$f"; then

            sed -i "/[[:space:]]*\[pam\]/a pam_cert_auth=True" "$f"

            found=true
    fi
done

# if section not in any file, append section with key = value to FIRST file in files parameter
if ! $found ; then
    file=$(echo "/etc/sssd/sssd.conf /etc/sssd/conf.d/*.conf" | cut -f1 -d ' ')
    mkdir -p "$(dirname "$file")"

    echo -e "[pam]\npam_cert_auth=True" >> "$file"

fi

umask $OLD_UMASK


if [ -f /usr/bin/authselect ]; then
    if ! authselect check; then
    echo "
    authselect integrity check failed. Remediation aborted!
    This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
    It is not recommended to manually edit the PAM files when authselect tool is available.
    In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
    exit 1
    fi
    authselect enable-feature with-smartcard

    authselect apply-changes -b
else
    

    if ! grep -qP "^\s*auth\s+sufficient\s+pam_sss.so\s*.*" "/etc/pam.d/smartcard-auth"; then
        # Line matching group + control + module was not found. Check group + module.
        if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/smartcard-auth")" -eq 1 ]; then
            # The control is updated only if one single line matches.
            sed -i -E --follow-symlinks "s/^(\s*auth\s+).*(\bpam_sss.so.*)/\1sufficient \2/" "/etc/pam.d/smartcard-auth"
        else
            echo "auth    sufficient    pam_sss.so" >> "/etc/pam.d/smartcard-auth"
        fi
    fi
    # Check the option
    if ! grep -qP "^\s*auth\s+sufficient\s+pam_sss.so\s*.*\sallow_missing_name\b" "/etc/pam.d/smartcard-auth"; then
        sed -i -E --follow-symlinks "/\s*auth\s+sufficient\s+pam_sss.so.*/ s/$/ allow_missing_name/" "/etc/pam.d/smartcard-auth"
    fi
    

    if ! grep -qP "^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.*" "/etc/pam.d/system-auth"; then
        # Line matching group + control + module was not found. Check group + module.
        if [ "$(grep -cP '^\s*auth\s+.*\s+pam_sss.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then
            # The control is updated only if one single line matches.
            sed -i -E --follow-symlinks "s/^(\s*auth\s+).*(\bpam_sss.so.*)/\1[success=done authinfo_unavail=ignore ignore=ignore default=die] \2/" "/etc/pam.d/system-auth"
        else
            echo "auth    [success=done authinfo_unavail=ignore ignore=ignore default=die]    pam_sss.so" >> "/etc/pam.d/system-auth"
        fi
    fi
    # Check the option
    if ! grep -qP "^\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so\s*.*\stry_cert_auth\b" "/etc/pam.d/system-auth"; then
        sed -i -E --follow-symlinks "/\s*auth\s+\[success=done authinfo_unavail=ignore ignore=ignore default=die\]\s+pam_sss.so.*/ s/$/ try_cert_auth/" "/etc/pam.d/system-auth"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:medium
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Test for domain group
  ansible.builtin.command: grep '^\s*\[domain\/[^]]*]' /etc/sssd/sssd.conf
  register: test_grep_domain
  failed_when: false
  changed_when: false
  check_mode: false
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Add default domain group (if no domain there)
  community.general.ini_file:
    path: /etc/sssd/sssd.conf
    section: '{{ item.section }}'
    option: '{{ item.option }}'
    value: '{{ item.value }}'
    create: true
    mode: 384
  with_items:
  - section: sssd
    option: domains
    value: default
  - section: domain/default
    option: id_provider
    value: files
  when:
  - '"sssd-common" in ansible_facts.packages'
  - test_grep_domain.stdout is defined
  - test_grep_domain.stdout | length < 1
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Enable Smartcards in SSSD
  community.general.ini_file:
    dest: /etc/sssd/sssd.conf
    section: pam
    option: pam_cert_auth
    value: 'True'
    create: true
    mode: 384
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Find all the conf files inside /etc/sssd/conf.d/
  ansible.builtin.find:
    paths: /etc/sssd/conf.d/
    patterns: '*.conf'
  register: sssd_conf_d_files
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Fix pam_cert_auth configuration in /etc/sssd/conf.d/
  ansible.builtin.replace:
    path: '{{ item.path }}'
    regexp: '[^#]*pam_cert_auth.*'
    replace: pam_cert_auth = True
  with_items: '{{ sssd_conf_d_files.files }}'
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Enable Smartcards in SSSD - Check if system relies on authselect
  ansible.builtin.stat:
    path: /usr/bin/authselect
  register: result_authselect_present
  when: '"sssd-common" in ansible_facts.packages'
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Enable Smartcards in SSSD - Remediate using authselect
  block:

  - name: Enable Smartcards in SSSD - Check integrity of authselect current profile
    ansible.builtin.command:
      cmd: authselect check
    register: result_authselect_check_cmd
    changed_when: false
    check_mode: false
    failed_when: false

  - name: Enable Smartcards in SSSD - Informative message based on the authselect
      integrity check result
    ansible.builtin.assert:
      that:
      - ansible_check_mode or result_authselect_check_cmd.rc == 0
      fail_msg:
      - authselect integrity check failed. Remediation aborted!
      - This remediation could not be applied because an authselect profile was not
        selected or the selected profile is not intact.
      - It is not recommended to manually edit the PAM files when authselect tool
        is available.
      - In cases where the default authselect profile does not cover a specific demand,
        a custom authselect profile is recommended.
      success_msg:
      - authselect integrity check passed

  - name: Enable Smartcards in SSSD - Get authselect current features
    ansible.builtin.shell:
      cmd: authselect current | tail -n+3 | awk '{ print $2 }'
    register: result_authselect_features
    changed_when: false
    check_mode: false
    when:
    - result_authselect_check_cmd is success

  - name: Enable Smartcards in SSSD - Ensure "with-smartcard" feature is enabled using
      authselect tool
    ansible.builtin.command:
      cmd: authselect enable-feature with-smartcard
    register: result_authselect_enable_feature_cmd
    when:
    - result_authselect_check_cmd is success
    - result_authselect_features.stdout is not search("with-smartcard")

  - name: Enable Smartcards in SSSD - Ensure authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_enable_feature_cmd is not skipped
    - result_authselect_enable_feature_cmd is success
  when:
  - '"sssd-common" in ansible_facts.packages'
  - result_authselect_present.stat.exists
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards

- name: Enable Smartcards in SSSD - Remediate by directly editing PAM files
  block:

  - name: Enable Smartcards in SSSD - Define a fact for control already filtered in
      case filters are used
    ansible.builtin.set_fact:
      pam_module_control: sufficient

  - name: Enable Smartcards in SSSD - Check if expected PAM module line is present
      in /etc/pam.d/smartcard-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/smartcard-auth
      regexp: ^\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so\s*.*
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_line_present

  - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/smartcard-auth
    block:

    - name: Enable Smartcards in SSSD - Check if required PAM module line is present
        in /etc/pam.d/smartcard-auth with different control
      ansible.builtin.lineinfile:
        path: /etc/pam.d/smartcard-auth
        regexp: ^\s*auth\s+.*\s+pam_sss.so\s*
        state: absent
      check_mode: true
      changed_when: false
      register: result_pam_line_other_control_present

    - name: Enable Smartcards in SSSD - Ensure the correct control for the required
        PAM module line in /etc/pam.d/smartcard-auth
      ansible.builtin.replace:
        dest: /etc/pam.d/smartcard-auth
        regexp: ^(\s*auth\s+).*(\bpam_sss.so.*)
        replace: \1{{ pam_module_control }} \2
      register: result_pam_module_edit
      when:
      - result_pam_line_other_control_present.found == 1

    - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included
        in /etc/pam.d/smartcard-auth
      ansible.builtin.lineinfile:
        dest: /etc/pam.d/smartcard-auth
        line: auth    {{ pam_module_control }}    pam_sss.so
      register: result_pam_module_add
      when:
      - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found
        > 1

    - name: Enable Smartcards in SSSD - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present is defined
      - result_authselect_present.stat.exists
      - |-
        (result_pam_module_add is defined and result_pam_module_add.changed)
         or (result_pam_module_edit is defined and result_pam_module_edit.changed)
    when:
    - result_pam_line_present.found is defined
    - result_pam_line_present.found == 0

  - name: Enable Smartcards in SSSD - Define a fact for control already filtered in
      case filters are used
    ansible.builtin.set_fact:
      pam_module_control: sufficient

  - name: Enable Smartcards in SSSD - Check if the required PAM module option is present
      in /etc/pam.d/smartcard-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/smartcard-auth
      regexp: ^\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so\s*.*\sallow_missing_name\b
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_module_sssd_enable_smartcards_option_present

  - name: Enable Smartcards in SSSD - Ensure the "allow_missing_name" PAM option for
      "pam_sss.so" is included in /etc/pam.d/smartcard-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/smartcard-auth
      backrefs: true
      regexp: ^(\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so.*)
      line: \1 allow_missing_name
      state: present
    register: result_pam_sssd_enable_smartcards_add
    when:
    - result_pam_module_sssd_enable_smartcards_option_present.found is defined
    - result_pam_module_sssd_enable_smartcards_option_present.found == 0

  - name: Enable Smartcards in SSSD - Define a fact for control already filtered in
      case filters are used
    ansible.builtin.set_fact:
      pam_module_control: '[success=done authinfo_unavail=ignore ignore=ignore default=die]'

  - name: Enable Smartcards in SSSD - Check if expected PAM module line is present
      in /etc/pam.d/system-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: ^\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so\s*.*
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_line_present

  - name: Enable Smartcards in SSSD - Include or update the PAM module line in /etc/pam.d/system-auth
    block:

    - name: Enable Smartcards in SSSD - Check if required PAM module line is present
        in /etc/pam.d/system-auth with different control
      ansible.builtin.lineinfile:
        path: /etc/pam.d/system-auth
        regexp: ^\s*auth\s+.*\s+pam_sss.so\s*
        state: absent
      check_mode: true
      changed_when: false
      register: result_pam_line_other_control_present

    - name: Enable Smartcards in SSSD - Ensure the correct control for the required
        PAM module line in /etc/pam.d/system-auth
      ansible.builtin.replace:
        dest: /etc/pam.d/system-auth
        regexp: ^(\s*auth\s+).*(\bpam_sss.so.*)
        replace: \1{{ pam_module_control }} \2
      register: result_pam_module_edit
      when:
      - result_pam_line_other_control_present.found == 1

    - name: Enable Smartcards in SSSD - Ensure the required PAM module line is included
        in /etc/pam.d/system-auth
      ansible.builtin.lineinfile:
        dest: /etc/pam.d/system-auth
        line: auth    {{ pam_module_control }}    pam_sss.so
      register: result_pam_module_add
      when:
      - result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found
        > 1

    - name: Enable Smartcards in SSSD - Ensure authselect changes are applied
      ansible.builtin.command:
        cmd: authselect apply-changes -b
      when:
      - result_authselect_present is defined
      - result_authselect_present.stat.exists
      - |-
        (result_pam_module_add is defined and result_pam_module_add.changed)
         or (result_pam_module_edit is defined and result_pam_module_edit.changed)
    when:
    - result_pam_line_present.found is defined
    - result_pam_line_present.found == 0

  - name: Enable Smartcards in SSSD - Define a fact for control already filtered in
      case filters are used
    ansible.builtin.set_fact:
      pam_module_control: '[success=done authinfo_unavail=ignore ignore=ignore default=die]'

  - name: Enable Smartcards in SSSD - Check if the required PAM module option is present
      in /etc/pam.d/system-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      regexp: ^\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so\s*.*\stry_cert_auth\b
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_module_sssd_enable_smartcards_option_present

  - name: Enable Smartcards in SSSD - Ensure the "try_cert_auth" PAM option for "pam_sss.so"
      is included in /etc/pam.d/system-auth
    ansible.builtin.lineinfile:
      path: /etc/pam.d/system-auth
      backrefs: true
      regexp: ^(\s*auth\s+{{ pam_module_control | regex_escape() }}\s+pam_sss.so.*)
      line: \1 try_cert_auth
      state: present
    register: result_pam_sssd_enable_smartcards_add
    when:
    - result_pam_module_sssd_enable_smartcards_option_present.found is defined
    - result_pam_module_sssd_enable_smartcards_option_present.found == 0
  when:
  - '"sssd-common" in ansible_facts.packages'
  - not result_authselect_present.stat.exists
  tags:
  - CCE-80909-5
  - DISA-STIG-RHEL-08-020250
  - PCI-DSS-Req-8.3
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed
  - sssd_enable_smartcards
OVAL test results details

tests the value of pam_cert_auth setting in the /etc/sssd/sssd.conf file  oval:ssg-test_sssd_enable_smartcards:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_enable_smartcards:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sssd/(sssd\.conf|conf.d/[^/]+\.conf)^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*pam_cert_auth[\s]*=[\s]*(\w+)\s*$1

tests the presence of try_cert_auth or require_cert_auth in /etc/pam.d/smartcard-auth  oval:ssg-test_sssd_enable_smartcards_allow_missing_name_smartcard_auth:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_enable_smartcards_smartcard_auth_options:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/pam.d/smartcard-auth^\s*auth.*?pam_sss\.so(.*)1

tests the presence of try_cert_auth or require_cert_auth in /etc/pam.d/system-auth  oval:ssg-test_sssd_enable_smartcards_cert_auth_system_auth:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/pam.d/system-authauth sufficient pam_sss.so forward_pass
SSSD Has a Correct Trust Anchorxccdf_org.ssgproject.content_rule_sssd_has_trust_anchor mediumCCE-86312-6

SSSD Has a Correct Trust Anchor

Rule IDxccdf_org.ssgproject.content_rule_sssd_has_trust_anchor
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-86312-6

References:
nistIA-5 (2) (a)
os-srgSRG-OS-000066-GPOS-00034, SRG-OS-000384-GPOS-00167
stigidRHEL-08-010090
stigrefSV-230229r1017048_rule
Description
SSSD must have acceptable trust anchor present.
Rationale
Without path validation, an informed trust decision by the relying party cannot be made when presented with any certificate not already explicitly trusted. A trust anchor is an authoritative entity represented via a public key and associated data. It is used in the context of public key infrastructures, X.509 digital certificates, and DNSSEC. When there is a chain of trust, usually the top entity to be trusted becomes the trust anchor; it can be, for example, a Certification Authority (CA). A certification path starts with the subject certificate and proceeds through a number of intermediate certificates up to a trusted root certificate, typically issued by a trusted CA. This requirement verifies that a certification path to an accepted trust anchor is used for certificate validation and that the path includes status information. Path validation is necessary for a relying party to make an informed trust decision when presented with any certificate not already explicitly trusted. Status information for certification paths includes certificate revocation lists or online certificate status protocol responses. Validation of the certificate status information is out of scope for this requirement.
Warnings
warning  Automatic remediation of this control is not available.
Evaluation messages
info 
No candidate or applicable check found.
Configure SSSD to Expire Offline Credentialsxccdf_org.ssgproject.content_rule_sssd_offline_cred_expiration mediumCCE-82460-7

Configure SSSD to Expire Offline Credentials

Rule IDxccdf_org.ssgproject.content_rule_sssd_offline_cred_expiration
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-sssd_offline_cred_expiration:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82460-7

References:
cis-csc1, 12, 15, 16, 5
cobit5DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10
isa-62443-20094.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4
isa-62443-2013SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1
iso27001-2013A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3
nistCM-6(a), IA-5(13)
nist-csfPR.AC-1, PR.AC-6, PR.AC-7
os-srgSRG-OS-000383-GPOS-00166
stigidRHEL-08-020290
stigrefSV-230376r1069307_rule
Description
SSSD should be configured to expire offline credentials after 1 day. Check if SSSD allows cached authentications with the following command:
$ sudo grep cache_credentials /etc/sssd/sssd.conf
cache_credentials = true
If "cache_credentials" is set to "false" or is missing no further checks are required.
To configure SSSD to expire offline credentials, set offline_credentials_expiration to 1 under the [pam] section in /etc/sssd/sssd.conf. For example:
[pam]
offline_credentials_expiration = 1
Rationale
If cached authentication information is out-of-date, the validity of the authentication information may be questionable.
OVAL test results details

tests the value of offline_credentials_expiration setting in the /etc/sssd/sssd.conf file  oval:ssg-test_sssd_offline_cred_expiration:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_offline_cred_expiration:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sssd/sssd.conf^[\s]*\[pam](?:[^\n\[]*\n+)+?[\s]*offline_credentials_expiration[\s]*=[\s]*(\d+)\s*(?:#.*)?$1

tests the value of cache_credentials setting in the /etc/sssd/sssd.conf file  oval:ssg-test_sssd_cache_credentials:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-obj_sssd_cache_credentials:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/sssd/sssd.conf^[\s]*cache_credentials\s*=\s*(\w+)\s*(?:#.*)?$1
Install usbguard Packagexccdf_org.ssgproject.content_rule_package_usbguard_installed mediumCCE-82959-8

Install usbguard Package

Rule IDxccdf_org.ssgproject.content_rule_package_usbguard_installed
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-package_usbguard_installed:def:1
Time2025-11-18T17:07:24-05:00
Severitymedium
Identifiers:

CCE-82959-8

References:
ism1418
nistCM-8(3), IA-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000378-GPOS-00163
app-srg-ctrSRG-APP-000141-CTR-000315
stigidRHEL-08-040139
stigrefSV-244547r1014811_rule
Description
The usbguard package can be installed with the following command:
$ sudo yum install usbguard
Rationale
usbguard is a software framework that helps to protect against rogue USB devices by implementing basic whitelisting/blacklisting capabilities based on USB device attributes.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if ( ! ( grep -sqE "^.*\.s390x$" /proc/sys/kernel/osrelease || grep -sqE "^s390x$" /proc/sys/kernel/arch; ) && rpm --quiet -q kernel ); then

if ! rpm -q --quiet "usbguard" ; then
    yum install -y "usbguard"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82959-8
  - DISA-STIG-RHEL-08-040139
  - NIST-800-53-CM-8(3)
  - NIST-800-53-IA-3
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_usbguard_installed

- name: Ensure usbguard is installed
  ansible.builtin.package:
    name: usbguard
    state: present
  when: ( ansible_architecture != "s390x" and "kernel" in ansible_facts.packages )
  tags:
  - CCE-82959-8
  - DISA-STIG-RHEL-08-040139
  - NIST-800-53-CM-8(3)
  - NIST-800-53-IA-3
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - package_usbguard_installed

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_usbguard

class install_usbguard {
  package { 'usbguard':
    ensure => 'installed',
  }
}

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=usbguard

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
  extensions:
    - usbguard


[[packages]]
name = "usbguard"
version = "*"

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package install usbguard

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

dnf install usbguard
OVAL test results details

package usbguard is installed  oval:ssg-test_package_usbguard_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_package_usbguard_installed:obj:1 of type rpminfo_object
Name
usbguard
Enable the USBGuard Servicexccdf_org.ssgproject.content_rule_service_usbguard_enabled mediumCCE-82853-3

Enable the USBGuard Service

Rule IDxccdf_org.ssgproject.content_rule_service_usbguard_enabled
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-service_usbguard_enabled:def:1
Time2025-11-18T17:07:29-05:00
Severitymedium
Identifiers:

CCE-82853-3

References:
ism1418
nistCM-8(3)(a), IA-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000378-GPOS-00163
app-srg-ctrSRG-APP-000141-CTR-000315
stigidRHEL-08-040141
stigrefSV-244548r1014815_rule
Description
The USBGuard service should be enabled. The usbguard service can be enabled with the following command:
$ sudo systemctl enable usbguard.service
Rationale
The usbguard service must be running in order to enforce the USB device authorization policy for all USB devices.

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# Remediation is applicable only in certain platforms
if ( ! ( grep -sqE "^.*\.s390x$" /proc/sys/kernel/osrelease || grep -sqE "^s390x$" /proc/sys/kernel/arch; ) && rpm --quiet -q kernel ); then

SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" unmask 'usbguard.service'
if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then
  "$SYSTEMCTL_EXEC" start 'usbguard.service'
fi
"$SYSTEMCTL_EXEC" enable 'usbguard.service'

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82853-3
  - DISA-STIG-RHEL-08-040141
  - NIST-800-53-CM-8(3)(a)
  - NIST-800-53-IA-3
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_usbguard_enabled

- name: Enable the USBGuard Service - Enable service usbguard
  block:

  - name: Gather the package facts
    ansible.builtin.package_facts:
      manager: auto

  - name: Enable the USBGuard Service - Enable Service usbguard
    ansible.builtin.systemd:
      name: usbguard
      enabled: true
      state: started
      masked: false
    when:
    - '"usbguard" in ansible_facts.packages'
  tags:
  - CCE-82853-3
  - DISA-STIG-RHEL-08-040141
  - NIST-800-53-CM-8(3)(a)
  - NIST-800-53-IA-3
  - enable_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - service_usbguard_enabled
  - special_service_block
  when: ( ansible_architecture != "s390x" and "kernel" in ansible_facts.packages )

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include enable_usbguard

class enable_usbguard {
  service {'usbguard':
    enable => true,
    ensure => 'running',
  }
}

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  annotations:
    complianceascode.io/depends-on: xccdf_org.ssgproject.content_rule_package_usbguard_installed
spec:
  config:
    ignition:
      version: 3.1.0
    systemd:
      units:
      - name: usbguard.service
        enabled: true


[customizations.services]
enabled = ["usbguard"]

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

service enable usbguard
OVAL test results details

package usbguard is installed  oval:ssg-test_service_usbguard_package_usbguard_installed:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_test_service_usbguard_package_usbguard_installed:obj:1 of type rpminfo_object
Name
usbguard

Test that the usbguard service is running  oval:ssg-test_service_running_usbguard:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_service_running_usbguard:obj:1 of type systemdunitproperty_object
UnitProperty
^usbguard\.(socket|service)$ActiveState

systemd test  oval:ssg-test_multi_user_wants_usbguard:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_usbguard_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Log USBGuard daemon audit events using Linux Auditxccdf_org.ssgproject.content_rule_configure_usbguard_auditbackend lowCCE-82168-6

Log USBGuard daemon audit events using Linux Audit

Rule IDxccdf_org.ssgproject.content_rule_configure_usbguard_auditbackend
Result
notapplicable
Multi-check ruleno
Time2025-11-18T17:07:29-05:00
Severitylow
Identifiers:

CCE-82168-6

References:
nistAU-2, CM-8(3), IA-3
osppFMT_SMF_EXT.1
os-srgSRG-OS-000062-GPOS-00031, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000141-CTR-000315
stigidRHEL-08-030603
stigrefSV-230470r1017261_rule
Description
To configure USBGuard daemon to log via Linux Audit (as opposed directly to a file), AuditBackend option in /etc/usbguard/usbguard-daemon.conf needs to be set to LinuxAudit.
Rationale
Using the Linux Audit logging allows for centralized trace of events.
Generate USBGuard Policyxccdf_org.ssgproject.content_rule_usbguard_generate_policy mediumCCE-83774-0

Generate USBGuard Policy

Rule IDxccdf_org.ssgproject.content_rule_usbguard_generate_policy
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-usbguard_generate_policy:def:1
Time2025-11-18T17:07:29-05:00
Severitymedium
Identifiers:

CCE-83774-0

References:
nistCM-8(3)(a), IA-3
os-srgSRG-OS-000378-GPOS-00163
stigidRHEL-08-040140
stigrefSV-230524r1014813_rule
Description
By default USBGuard when enabled prevents access to all USB devices and this lead to inaccessible system if they use USB mouse/keyboard. To prevent this scenario, the initial policy configuration must be generated based on current connected USB devices.
Rationale
The usbguard must be configured to allow connected USB devices to work properly, avoiding the system to become inaccessible.

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
# Remediation is applicable only in certain platforms
if ( ! ( grep -sqE "^.*\.s390x$" /proc/sys/kernel/osrelease || grep -sqE "^s390x$" /proc/sys/kernel/arch; ) && rpm --quiet -q kernel ); then

if rpm --quiet -q usbguard
then
    USBGUARD_CONF=/etc/usbguard/rules.conf
    if [ ! -f "$USBGUARD_CONF" ] || [ ! -s "$USBGUARD_CONF" ]; then
        usbguard generate-policy > $USBGUARD_CONF
        if [ ! -s "$USBGUARD_CONF" ]; then
            # make sure OVAL check doesn't fail on systems where
            # generate-policy doesn't find any USB devices (for
            # example a system might not have a USB bus)
            echo "# No USB devices found" > $USBGUARD_CONF
        fi
        # make sure it has correct permissions
        chmod 600 $USBGUARD_CONF

        SYSTEMCTL_EXEC='/usr/bin/systemctl'
        "$SYSTEMCTL_EXEC" unmask 'usbguard.service'
        "$SYSTEMCTL_EXEC" restart 'usbguard.service'
        "$SYSTEMCTL_EXEC" enable 'usbguard.service'
    fi
else
    echo "USBGuard is not installed. No remediation was applied!"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83774-0
  - DISA-STIG-RHEL-08-040140
  - NIST-800-53-CM-8(3)(a)
  - NIST-800-53-IA-3
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - usbguard_generate_policy

- name: Generate USBGuard Policy
  block:

  - name: Gather the package facts
    ansible.builtin.package_facts:
      manager: auto

  - name: Check that the /etc/usbguard/rules.conf exists
    ansible.builtin.stat:
      path: /etc/usbguard/rules.conf
    register: policy_file

  - name: Create USBGuard Policy configuration
    ansible.builtin.command: usbguard generate-policy
    register: policy
    when: not policy_file.stat.exists or policy_file.stat.size == 0

  - name: Copy the Generated Policy configuration to a persistent file
    ansible.builtin.copy:
      content: '{{ policy.stdout }}'
      dest: /etc/usbguard/rules.conf
      mode: 384
    when: not policy_file.stat.exists or policy_file.stat.size == 0

  - name: Add comment into /etc/usbguard/rules.conf when system has no USB devices
    ansible.builtin.lineinfile:
      path: /etc/usbguard/rules.conf
      line: '# No USB devices found'
      state: present
    when: not policy_file.stat.exists or policy_file.stat.size == 0

  - name: Enable service usbguard
    ansible.builtin.systemd:
      name: usbguard
      enabled: 'yes'
      state: started
      masked: 'no'
  when:
  - ( ansible_architecture != "s390x" and "kernel" in ansible_facts.packages )
  - '"usbguard" in ansible_facts.packages'
  tags:
  - CCE-83774-0
  - DISA-STIG-RHEL-08-040140
  - NIST-800-53-CM-8(3)(a)
  - NIST-800-53-IA-3
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - usbguard_generate_policy
OVAL test results details

Check the usbguard rules in either /etc/usbguard/rules.conf or /etc/usbguard/rules.d/ contain at least one non whitespace character and exists  oval:ssg-test_usbguard_rules_nonempty:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-obj_usbguard_rules_nonempty:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/usbguard/(rules|rules\.d/.*)\.conf$^.*\S+.*$1
Disable graphical user interfacexccdf_org.ssgproject.content_rule_xwindows_remove_packages mediumCCE-83411-9

Disable graphical user interface

Rule IDxccdf_org.ssgproject.content_rule_xwindows_remove_packages
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-xwindows_remove_packages:def:1
Time2025-11-18T17:07:29-05:00
Severitymedium
Identifiers:

CCE-83411-9

References:
nistCM-6(b)
os-srgSRG-OS-000480-GPOS-00227
stigidRHEL-08-040320
stigrefSV-230553r1017315_rule
Description
By removing the following packages, the system no longer has X Windows installed. xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland If X Windows is not installed then the system cannot boot into graphical user mode. This prevents the system from being accidentally or maliciously booted into a graphical.target mode. To do so, run the following command:
sudo yum remove xorg-x11-server-Xorg xorg-x11-server-common xorg-x11-server-utils xorg-x11-server-Xwayland
Rationale
Unnecessary service packages must not be installed to decrease the attack surface of the system. X windows has a long history of security vulnerabilities and should not be installed unless approved and documented.
Warnings
warning  The installation and use of a Graphical User Interface (GUI) increases your attack vector and decreases your overall security posture. Removing the package xorg-x11-server-common package will remove the graphical target which might bring your system to an inconsistent state requiring additional configuration to access the system again. The rule xwindows_runlevel_target can be used to configure the system to boot into the multi-user.target. If a GUI is an operational requirement, a tailored profile that removes this rule should be used before continuing installation.
warning  This rule is disabled on Red Hat Virtualization Hosts and Managers, it will report not applicable. X11 graphic libraries are dependency of OpenStack Cinderlib storage provider.

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict

# remove packages

if rpm -q --quiet "xorg-x11-server-Xorg" ; then
yum remove -y "xorg-x11-server-Xorg"
fi

if rpm -q --quiet "xorg-x11-server-common" ; then
yum remove -y "xorg-x11-server-common"
fi

if rpm -q --quiet "xorg-x11-server-utils" ; then
yum remove -y "xorg-x11-server-utils"
fi

if rpm -q --quiet "xorg-x11-server-Xwayland" ; then
yum remove -y "xorg-x11-server-Xwayland"
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Disable graphical user interface - Ensure xorg-x11-server-Xorg is removed
  ansible.builtin.package:
    name: xorg-x11-server-Xorg
    state: absent
  tags:
  - CCE-83411-9
  - DISA-STIG-RHEL-08-040320
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_remove_packages

- name: Disable graphical user interface - Ensure xorg-x11-server-common is removed
  ansible.builtin.package:
    name: xorg-x11-server-common
    state: absent
  tags:
  - CCE-83411-9
  - DISA-STIG-RHEL-08-040320
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_remove_packages

- name: Disable graphical user interface - Ensure xorg-x11-server-utils is removed
  ansible.builtin.package:
    name: xorg-x11-server-utils
    state: absent
  tags:
  - CCE-83411-9
  - DISA-STIG-RHEL-08-040320
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_remove_packages

- name: Disable graphical user interface - Ensure xorg-x11-server-Xwayland is removed
  ansible.builtin.package:
    name: xorg-x11-server-Xwayland
    state: absent
  tags:
  - CCE-83411-9
  - DISA-STIG-RHEL-08-040320
  - NIST-800-53-CM-6(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_remove_packages


# remove packages

package --remove=xorg-x11-server-Xorg

package --remove=xorg-x11-server-common

package --remove=xorg-x11-server-utils

package --remove=xorg-x11-server-Xwayland
OVAL test results details

package xorg-x11-server-Xorg is removed  oval:ssg-test_package_xorg-x11-server-Xorg_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedxorg-x11-server-Xorgx86_64(none)22.el81.20.110:1.20.11-22.el8199e2f91fd431d51xorg-x11-server-Xorg-0:1.20.11-22.el8.x86_64

package xorg-x11-server-common is removed  oval:ssg-test_package_xorg-x11-server-common_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedxorg-x11-server-commonx86_64(none)22.el81.20.110:1.20.11-22.el8199e2f91fd431d51xorg-x11-server-common-0:1.20.11-22.el8.x86_64

package xorg-x11-server-utils is removed  oval:ssg-test_package_xorg-x11-server-utils_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedxorg-x11-server-utilsx86_64(none)27.el87.70:7.7-27.el8199e2f91fd431d51xorg-x11-server-utils-0:7.7-27.el8.x86_64

package xorg-x11-server-Xwayland is removed  oval:ssg-test_package_xorg-x11-server-Xwayland_removed:tst:1  false

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedxorg-x11-server-Xwaylandx86_64(none)15.el821.1.30:21.1.3-15.el8199e2f91fd431d51xorg-x11-server-Xwayland-0:21.1.3-15.el8.x86_64
Disable Graphical Environment Startup By Setting Default Targetxccdf_org.ssgproject.content_rule_xwindows_runlevel_target mediumCCE-83380-6

Disable Graphical Environment Startup By Setting Default Target

Rule IDxccdf_org.ssgproject.content_rule_xwindows_runlevel_target
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-xwindows_runlevel_target:def:1
Time2025-11-18T17:07:29-05:00
Severitymedium
Identifiers:

CCE-83380-6

References:
cis-csc12, 15, 8
cobit5APO13.01, DSS01.04, DSS05.02, DSS05.03
isa-62443-20094.3.3.6.6
isa-62443-2013SR 1.13, SR 2.6, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.13.1.1, A.13.2.1, A.14.1.3, A.6.2.1, A.6.2.2
nistCM-7(a), CM-7(b), CM-6(a)
nist-csfPR.AC-3, PR.PT-4
os-srgSRG-OS-000480-GPOS-00227
cis2.2.20
stigidRHEL-08-040321
stigrefSV-251718r1017371_rule
Description
Systems that do not require a graphical user interface should only boot by default into multi-user.target mode. This prevents accidental booting of the system into a graphical.target mode. Setting the system's default target to multi-user.target will prevent automatic startup of the graphical environment. To do so, run:
$ systemctl set-default multi-user.target
You should see the following output:
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target.
Rationale
Services that are not required for system and application processes must not be active to decrease the attack surface of the system.

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel; then

systemctl set-default multi-user.target

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83380-6
  - DISA-STIG-RHEL-08-040321
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_runlevel_target

- name: Switch to multi-user runlevel
  ansible.builtin.file:
    src: /usr/lib/systemd/system/multi-user.target
    dest: /etc/systemd/system/default.target
    state: link
    force: true
  when: '"kernel" in ansible_facts.packages'
  tags:
  - CCE-83380-6
  - DISA-STIG-RHEL-08-040321
  - NIST-800-53-CM-6(a)
  - NIST-800-53-CM-7(a)
  - NIST-800-53-CM-7(b)
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
  - xwindows_runlevel_target
OVAL test results details

default.target systemd softlink exists  oval:ssg-test_disable_xwindows_runlevel_target:tst:1  false

Following items have been found on the system:
Result of item-state comparisonFilepathCanonical path
false/etc/systemd/system/default.target/usr/lib/systemd/system/graphical.target
Record Events that Modify the System's Discretionary Access Controls - chmodxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod mediumCCE-80685-1

Record Events that Modify the System's Discretionary Access Controls - chmod

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_chmod:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80685-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030490
stigrefSV-230456r1017253_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="chmod"
	KEY="perm_mod"
	SYSCALL_GROUPING="chmod fchmod fchmodat"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80685-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit chmod tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80685-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for chmod for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of chmod in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of chmod in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80685-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for chmod for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of chmod in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of chmod in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80685-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit chmod  oval:ssg-test_32bit_ardm_chmod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_chmod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit chmod  oval:ssg-test_64bit_ardm_chmod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_chmod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit chmod  oval:ssg-test_32bit_ardm_chmod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_chmod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit chmod  oval:ssg-test_64bit_ardm_chmod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_chmod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chmod[\s]+|([\s]+|[,])chmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - chownxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown mediumCCE-80686-9

Record Events that Modify the System's Discretionary Access Controls - chown

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_chown:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80686-9

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030480
stigrefSV-230455r1017251_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="chown"
	KEY="perm_mod"
	SYSCALL_GROUPING="chown fchown fchownat lchown"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80686-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit chown tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80686-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for chown for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of chown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of chown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80686-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for chown for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of chown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - chown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of chown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80686-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_chown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit chown  oval:ssg-test_32bit_ardm_chown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_chown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit chown  oval:ssg-test_64bit_ardm_chown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_chown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit chown  oval:ssg-test_32bit_ardm_chown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_chown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit chown  oval:ssg-test_64bit_ardm_chown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_chown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+chown[\s]+|([\s]+|[,])chown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fchmodxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmod mediumCCE-80687-7

Record Events that Modify the System's Discretionary Access Controls - fchmod

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmod
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fchmod:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80687-7

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030490
stigrefSV-230456r1017253_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fchmod"
	KEY="perm_mod"
	SYSCALL_GROUPING="chmod fchmod fchmodat"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80687-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fchmod tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80687-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchmod for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmod in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmod in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80687-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchmod for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmod in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmod
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmod in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80687-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmod
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fchmod  oval:ssg-test_32bit_ardm_fchmod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchmod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fchmod  oval:ssg-test_64bit_ardm_fchmod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchmod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fchmod  oval:ssg-test_32bit_ardm_fchmod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchmod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fchmod  oval:ssg-test_64bit_ardm_fchmod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchmod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmod[\s]+|([\s]+|[,])fchmod([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fchmodatxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmodat mediumCCE-80688-5

Record Events that Modify the System's Discretionary Access Controls - fchmodat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmodat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fchmodat:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80688-5

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030490
stigrefSV-230456r1017253_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fchmodat"
	KEY="perm_mod"
	SYSCALL_GROUPING="chmod fchmod fchmodat"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80688-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmodat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fchmodat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80688-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmodat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchmodat for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmodat
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmodat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmodat
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmodat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80688-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmodat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchmodat for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmodat
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmodat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchmodat
      syscall_grouping:
      - chmod
      - fchmod
      - fchmodat

  - name: Check existence of fchmodat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80688-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030490
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchmodat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fchmodat  oval:ssg-test_32bit_ardm_fchmodat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchmodat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fchmodat  oval:ssg-test_64bit_ardm_fchmodat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchmodat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fchmodat  oval:ssg-test_32bit_ardm_fchmodat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchmodat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fchmodat  oval:ssg-test_64bit_ardm_fchmodat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchmodat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchmodat[\s]+|([\s]+|[,])fchmodat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fchownxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchown mediumCCE-80689-3

Record Events that Modify the System's Discretionary Access Controls - fchown

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchown
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fchown:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80689-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030480
stigrefSV-230455r1017251_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fchown"
	KEY="perm_mod"
	SYSCALL_GROUPING="chown fchown fchownat lchown"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80689-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fchown tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80689-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchown for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80689-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchown for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80689-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fchown  oval:ssg-test_32bit_ardm_fchown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fchown  oval:ssg-test_64bit_ardm_fchown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fchown  oval:ssg-test_32bit_ardm_fchown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fchown  oval:ssg-test_64bit_ardm_fchown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchown[\s]+|([\s]+|[,])fchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fchownatxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchownat mediumCCE-80690-1

Record Events that Modify the System's Discretionary Access Controls - fchownat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchownat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fchownat:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80690-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030480
stigrefSV-230455r1017251_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fchownat"
	KEY="perm_mod"
	SYSCALL_GROUPING="chown fchown fchownat lchown"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80690-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchownat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fchownat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80690-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchownat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchownat for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchownat
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchownat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchownat
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchownat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80690-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchownat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fchownat for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchownat
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchownat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fchownat
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of fchownat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80690-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fchownat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fchownat  oval:ssg-test_32bit_ardm_fchownat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchownat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fchownat  oval:ssg-test_64bit_ardm_fchownat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchownat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fchownat  oval:ssg-test_32bit_ardm_fchownat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fchownat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fchownat  oval:ssg-test_64bit_ardm_fchownat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fchownat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fchownat[\s]+|([\s]+|[,])fchownat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fremovexattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fremovexattr mediumCCE-80691-9

Record Events that Modify the System's Discretionary Access Controls - fremovexattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fremovexattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fremovexattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80691-9

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000462-GPOS-00206, SRG-OS-000463-GPOS-00207, SRG-OS-000471-GPOS-00215, SRG-OS-000474-GPOS-00219, SRG-OS-000466-GPOS-00210, SRG-OS-000468-GPOS-00212, SRG-OS-000064-GPOS-00033
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root.

If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod


If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S fremovexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S fremovexattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fremovexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="fremovexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80691-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fremovexattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80691-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fremovexattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80691-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fremovexattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80691-9
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fremovexattr  oval:ssg-test_32bit_ardm_fremovexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fremovexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit fremovexattr auid=0  oval:ssg-test_32bit_ardm_fremovexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fremovexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fremovexattr  oval:ssg-test_64bit_ardm_fremovexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fremovexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit fremovexattr  oval:ssg-test_64bit_ardm_fremovexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fremovexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fremovexattr  oval:ssg-test_32bit_ardm_fremovexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fremovexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit fremovexattr  oval:ssg-test_32bit_ardm_fremovexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fremovexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fremovexattr  oval:ssg-test_64bit_ardm_fremovexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fremovexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit fremovexattr  oval:ssg-test_64bit_ardm_fremovexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fremovexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fremovexattr[\s]+|([\s]+|[,])fremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - fsetxattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fsetxattr mediumCCE-80692-7

Record Events that Modify the System's Discretionary Access Controls - fsetxattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fsetxattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_fsetxattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80692-7

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000462-GPOS-00206, SRG-OS-000463-GPOS-00207, SRG-OS-000466-GPOS-00210, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215, SRG-OS-000474-GPOS-00219, SRG-OS-000064-GPOS-00033
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S fsetxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S fsetxattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="fsetxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="fsetxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80692-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit fsetxattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80692-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fsetxattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80692-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for fsetxattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - fsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of fsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80692-7
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_fsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit fsetxattr  oval:ssg-test_32bit_ardm_fsetxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fsetxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit fsetxattr auid=0  oval:ssg-test_32bit_ardm_fsetxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fsetxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit fsetxattr  oval:ssg-test_64bit_ardm_fsetxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fsetxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit fsetxattr  oval:ssg-test_64bit_ardm_fsetxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fsetxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit fsetxattr  oval:ssg-test_32bit_ardm_fsetxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fsetxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit fsetxattr  oval:ssg-test_32bit_ardm_fsetxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_fsetxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit fsetxattr  oval:ssg-test_64bit_ardm_fsetxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fsetxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit fsetxattr  oval:ssg-test_64bit_ardm_fsetxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_fsetxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+fsetxattr[\s]+|([\s]+|[,])fsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - lchownxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lchown mediumCCE-80693-5

Record Events that Modify the System's Discretionary Access Controls - lchown

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lchown
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_lchown:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80693-5

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000466-GPOS-00210, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030480
stigrefSV-230455r1017251_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="lchown"
	KEY="perm_mod"
	SYSCALL_GROUPING="chown fchown fchownat lchown"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80693-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit lchown tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80693-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lchown for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of lchown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of lchown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80693-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lchown for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of lchown in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lchown
      syscall_grouping:
      - chown
      - fchown
      - fchownat
      - lchown

  - name: Check existence of lchown in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80693-5
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030480
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lchown
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit lchown  oval:ssg-test_32bit_ardm_lchown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lchown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit lchown  oval:ssg-test_64bit_ardm_lchown_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lchown_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit lchown  oval:ssg-test_32bit_ardm_lchown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lchown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit lchown  oval:ssg-test_64bit_ardm_lchown_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lchown_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lchown[\s]+|([\s]+|[,])lchown([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - lremovexattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lremovexattr mediumCCE-80694-3

Record Events that Modify the System's Discretionary Access Controls - lremovexattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lremovexattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_lremovexattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80694-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000462-GPOS-00206, SRG-OS-000463-GPOS-00207, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215, SRG-OS-000474-GPOS-00219, SRG-OS-000466-GPOS-00210, SRG-OS-000064-GPOS-00033
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root.

If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod


If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S lremovexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S lremovexattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="lremovexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="lremovexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80694-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit lremovexattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80694-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lremovexattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80694-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lremovexattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lremovexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lremovexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80694-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lremovexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit lremovexattr  oval:ssg-test_32bit_ardm_lremovexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lremovexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit lremovexattr auid=0  oval:ssg-test_32bit_ardm_lremovexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lremovexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit lremovexattr  oval:ssg-test_64bit_ardm_lremovexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lremovexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit lremovexattr  oval:ssg-test_64bit_ardm_lremovexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lremovexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit lremovexattr  oval:ssg-test_32bit_ardm_lremovexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lremovexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit lremovexattr  oval:ssg-test_32bit_ardm_lremovexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lremovexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit lremovexattr  oval:ssg-test_64bit_ardm_lremovexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lremovexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit lremovexattr  oval:ssg-test_64bit_ardm_lremovexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lremovexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lremovexattr[\s]+|([\s]+|[,])lremovexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - lsetxattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lsetxattr mediumCCE-80695-0

Record Events that Modify the System's Discretionary Access Controls - lsetxattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lsetxattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_lsetxattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80695-0

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000462-GPOS-00206, SRG-OS-000463-GPOS-00207, SRG-OS-000466-GPOS-00210, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215, SRG-OS-000474-GPOS-00219, SRG-OS-000064-GPOS-00033
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S lsetxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S lsetxattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="lsetxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="lsetxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80695-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit lsetxattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80695-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lsetxattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80695-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for lsetxattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - lsetxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of lsetxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80695-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_lsetxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit lsetxattr  oval:ssg-test_32bit_ardm_lsetxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lsetxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit lsetxattr auid=0  oval:ssg-test_32bit_ardm_lsetxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lsetxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit lsetxattr  oval:ssg-test_64bit_ardm_lsetxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lsetxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit lsetxattr  oval:ssg-test_64bit_ardm_lsetxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lsetxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit lsetxattr  oval:ssg-test_32bit_ardm_lsetxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lsetxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit lsetxattr  oval:ssg-test_32bit_ardm_lsetxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_lsetxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit lsetxattr  oval:ssg-test_64bit_ardm_lsetxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lsetxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit lsetxattr  oval:ssg-test_64bit_ardm_lsetxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_lsetxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+lsetxattr[\s]+|([\s]+|[,])lsetxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - removexattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_removexattr mediumCCE-80696-8

Record Events that Modify the System's Discretionary Access Controls - removexattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_removexattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_removexattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80696-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000462-GPOS-00206, SRG-OS-000463-GPOS-00207, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215, SRG-OS-000474-GPOS-00219, SRG-OS-000466-GPOS-00210, SRG-OS-000064-GPOS-00033
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root.

If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod


If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S removexattr -F auid=0 -F key=perm_mod


If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S removexattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="removexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="removexattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80696-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_removexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit removexattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80696-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_removexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for removexattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80696-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_removexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for removexattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - removexattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of removexattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80696-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_removexattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit removexattr  oval:ssg-test_32bit_ardm_removexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_removexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit removexattr auid=0  oval:ssg-test_32bit_ardm_removexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_removexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit removexattr  oval:ssg-test_64bit_ardm_removexattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_removexattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit removexattr  oval:ssg-test_64bit_ardm_removexattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_removexattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit removexattr  oval:ssg-test_32bit_ardm_removexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_removexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit removexattr  oval:ssg-test_32bit_ardm_removexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_removexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit removexattr  oval:ssg-test_64bit_ardm_removexattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_removexattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit removexattr  oval:ssg-test_64bit_ardm_removexattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_removexattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+removexattr[\s]+|([\s]+|[,])removexattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify the System's Discretionary Access Controls - setxattrxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_setxattr mediumCCE-80697-6

Record Events that Modify the System's Discretionary Access Controls - setxattr

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_dac_modification_setxattr
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_dac_modification_setxattr:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80697-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.5.5
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000466-GPOS-00210, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203
app-srg-ctrSRG-APP-000091-CTR-000160, SRG-APP-000492-CTR-001220, SRG-APP-000493-CTR-001225, SRG-APP-000494-CTR-001230, SRG-APP-000500-CTR-001260, SRG-APP-000507-CTR-001295, SRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.9
pcidss410.3.4, 10.3
stigidRHEL-08-030200
stigrefSV-230413r1017219_rule
Description
At a minimum, the audit system should collect file permission changes for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S setxattr -F auid=0 -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S setxattr -F auid=0 -F key=perm_mod
Rationale
The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="setxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done



for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid=0"
	SYSCALL="setxattr"
	KEY="perm_mod"
	SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr"

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80697-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_setxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit setxattr tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80697-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_setxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for setxattr for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80697-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_setxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for setxattr for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/perm_mod.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/perm_mod.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - setxattr
      syscall_grouping:
      - fremovexattr
      - lremovexattr
      - removexattr
      - fsetxattr
      - lsetxattr
      - setxattr

  - name: Check existence of setxattr in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid=0 (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid=0 (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid=0 -F
        key=perm_mod
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80697-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030200
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.5
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.4
  - audit_rules_dac_modification_setxattr
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit setxattr  oval:ssg-test_32bit_ardm_setxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_setxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit setxattr auid=0  oval:ssg-test_32bit_ardm_setxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_setxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit setxattr  oval:ssg-test_64bit_ardm_setxattr_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_setxattr_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit setxattr  oval:ssg-test_64bit_ardm_setxattr_augenrules_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_setxattr_augenrules_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit setxattr  oval:ssg-test_32bit_ardm_setxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_setxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit setxattr  oval:ssg-test_32bit_ardm_setxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_setxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit setxattr  oval:ssg-test_64bit_ardm_setxattr_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_setxattr_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit setxattr  oval:ssg-test_64bit_ardm_setxattr_auditctl_auid_0:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_setxattr_auditctl_auid_0:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+setxattr[\s]+|([\s]+|[,])setxattr([\s]+|[,])))(?:.*-F\s+auid=0[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run chaclxccdf_org.ssgproject.content_rule_audit_rules_execution_chacl mediumCCE-89446-9

Record Any Attempts to Run chacl

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_chacl
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_chacl:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-89446-9

References:
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
cis5.2.3.17
stigidRHEL-08-030570
stigrefSV-230464r1017256_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. Audit records can be generated from various components within the information system (e.g., module or policy filter).

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/chacl -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-89446-9
  - DISA-STIG-RHEL-08-030570
  - audit_rules_execution_chacl
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run chacl - Perform remediation of Audit rules for
    /usr/bin/chacl
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chacl -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-89446-9
  - DISA-STIG-RHEL-08-030570
  - audit_rules_execution_chacl
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules chacl  oval:ssg-test_audit_rules_execution_chacl_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_chacl_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl chacl  oval:ssg-test_audit_rules_execution_chacl_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_chacl_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run setfaclxccdf_org.ssgproject.content_rule_audit_rules_execution_setfacl mediumCCE-88437-9

Record Any Attempts to Run setfacl

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_setfacl
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_setfacl:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-88437-9

References:
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
cis5.2.3.16
stigidRHEL-08-030330
stigrefSV-230435r1017236_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. Audit records can be generated from various components within the information system (e.g., module or policy filter).

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/setfacl -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-88437-9
  - DISA-STIG-RHEL-08-030330
  - audit_rules_execution_setfacl
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run setfacl - Perform remediation of Audit rules for
    /usr/bin/setfacl
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/setfacl -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-88437-9
  - DISA-STIG-RHEL-08-030330
  - audit_rules_execution_setfacl
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules setfacl  oval:ssg-test_audit_rules_execution_setfacl_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setfacl_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl setfacl  oval:ssg-test_audit_rules_execution_setfacl_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setfacl_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/setfacl(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run chconxccdf_org.ssgproject.content_rule_audit_rules_execution_chcon mediumCCE-80698-4

Record Any Attempts to Run chcon

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_chcon
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_chcon:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80698-4

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215, SRG-OS-000463-GPOS-00207, SRG-OS-000465-GPOS-00209
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
cis5.2.3.15
stigidRHEL-08-030260
stigrefSV-230419r1017221_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/chcon -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80698-4
  - DISA-STIG-RHEL-08-030260
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_chcon
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run chcon - Perform remediation of Audit rules for
    /usr/bin/chcon
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chcon -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80698-4
  - DISA-STIG-RHEL-08-030260
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_chcon
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules chcon  oval:ssg-test_audit_rules_execution_chcon_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_chcon_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl chcon  oval:ssg-test_audit_rules_execution_chcon_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_chcon_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chcon(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run semanagexccdf_org.ssgproject.content_rule_audit_rules_execution_semanage mediumCCE-80700-8

Record Any Attempts to Run semanage

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_semanage
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_semanage:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80700-8

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000463-GPOS-00207, SRG-OS-000465-GPOS-00209
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250
stigidRHEL-08-030313
stigrefSV-230429r1017230_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/semanage -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80700-8
  - DISA-STIG-RHEL-08-030313
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_semanage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run semanage - Perform remediation of Audit rules for
    /usr/sbin/semanage
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/semanage -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/semanage -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80700-8
  - DISA-STIG-RHEL-08-030313
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_semanage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules semanage  oval:ssg-test_audit_rules_execution_semanage_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_semanage_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl semanage  oval:ssg-test_audit_rules_execution_semanage_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_semanage_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/semanage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run setfilesxccdf_org.ssgproject.content_rule_audit_rules_execution_setfiles mediumCCE-82280-9

Record Any Attempts to Run setfiles

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_setfiles
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_setfiles:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-82280-9

References:
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000463-GPOS-00207, SRG-OS-000465-GPOS-00209
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250
stigidRHEL-08-030314
stigrefSV-230430r1017231_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/setfiles -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82280-9
  - DISA-STIG-RHEL-08-030314
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_setfiles
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run setfiles - Perform remediation of Audit rules for
    /usr/sbin/setfiles
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/setfiles -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setfiles -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82280-9
  - DISA-STIG-RHEL-08-030314
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_setfiles
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules setfiles  oval:ssg-test_audit_rules_execution_setfiles_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setfiles_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl setfiles  oval:ssg-test_audit_rules_execution_setfiles_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setfiles_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setfiles(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run setseboolxccdf_org.ssgproject.content_rule_audit_rules_execution_setsebool mediumCCE-80701-6

Record Any Attempts to Run setsebool

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_execution_setsebool
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_execution_setsebool:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80701-6

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000463-GPOS-00207, SRG-OS-000465-GPOS-00209
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250
stigidRHEL-08-030316
stigrefSV-230432r1017233_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/setsebool -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80701-6
  - DISA-STIG-RHEL-08-030316
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_setsebool
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run setsebool - Perform remediation of Audit rules
    for /usr/sbin/setsebool
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/setsebool -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/setsebool -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80701-6
  - DISA-STIG-RHEL-08-030316
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_execution_setsebool
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules setsebool  oval:ssg-test_audit_rules_execution_setsebool_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setsebool_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl setsebool  oval:ssg-test_audit_rules_execution_setsebool_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_execution_setsebool_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/setsebool(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects File Deletion Events by User - renamexccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rename mediumCCE-80703-2

Ensure auditd Collects File Deletion Events by User - rename

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rename
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_file_deletion_events_rename:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80703-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.4, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.1.1, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.MA-2, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000467-GPOS-00211, SRG-OS-000468-GPOS-00212
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.13
pcidss410.2.1.7, 10.2.1, 10.2
stigidRHEL-08-030361
stigrefSV-230439r1017243_rule
Description
At a minimum, the audit system should collect file deletion events for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S rename -F auid>=1000 -F auid!=unset -F key=delete
Rationale
Auditing file deletions will create an audit trail for files that are removed from the system. The audit trail could aid in system troubleshooting, as well as, detecting malicious processes that attempt to delete log files to conceal their presence.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="rename"
	KEY="delete"
	SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80703-2
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rename
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit rename tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80703-2
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rename
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for rename for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rename
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rename in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rename
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rename in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80703-2
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rename
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for rename for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rename
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rename in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rename
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rename in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80703-2
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rename
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit rename  oval:ssg-test_32bit_ardm_rename_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_rename_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit rename  oval:ssg-test_64bit_ardm_rename_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_rename_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit rename  oval:ssg-test_32bit_ardm_rename_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_rename_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit rename  oval:ssg-test_64bit_ardm_rename_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_rename_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rename[\s]+|([\s]+|[,])rename([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects File Deletion Events by User - renameatxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_renameat mediumCCE-80704-0

Ensure auditd Collects File Deletion Events by User - renameat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_renameat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_file_deletion_events_renameat:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80704-0

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.4, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.1.1, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.MA-2, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000467-GPOS-00211, SRG-OS-000468-GPOS-00212
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.13
pcidss410.2.1.7, 10.2.1, 10.2
stigidRHEL-08-030361
stigrefSV-230439r1017243_rule
Description
At a minimum, the audit system should collect file deletion events for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S renameat -F auid>=1000 -F auid!=unset -F key=delete
Rationale
Auditing file deletions will create an audit trail for files that are removed from the system. The audit trail could aid in system troubleshooting, as well as, detecting malicious processes that attempt to delete log files to conceal their presence.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="renameat"
	KEY="delete"
	SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80704-0
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_renameat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit renameat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80704-0
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_renameat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for renameat for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - renameat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of renameat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - renameat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of renameat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80704-0
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_renameat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for renameat for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - renameat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of renameat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - renameat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of renameat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80704-0
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_renameat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit renameat  oval:ssg-test_32bit_ardm_renameat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_renameat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit renameat  oval:ssg-test_64bit_ardm_renameat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_renameat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit renameat  oval:ssg-test_32bit_ardm_renameat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_renameat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit renameat  oval:ssg-test_64bit_ardm_renameat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_renameat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+renameat[\s]+|([\s]+|[,])renameat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects File Deletion Events by User - rmdirxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rmdir mediumCCE-80705-7

Ensure auditd Collects File Deletion Events by User - rmdir

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rmdir
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_file_deletion_events_rmdir:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80705-7

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.4, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.1.1, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.MA-2, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000467-GPOS-00211, SRG-OS-000468-GPOS-00212
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
pcidss410.2.1.7, 10.2.1, 10.2
stigidRHEL-08-030361
stigrefSV-230439r1017243_rule
Description
At a minimum, the audit system should collect file deletion events for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S rmdir -F auid>=1000 -F auid!=unset -F key=delete
Rationale
Auditing file deletions will create an audit trail for files that are removed from the system. The audit trail could aid in system troubleshooting, as well as, detecting malicious processes that attempt to delete log files to conceal their presence.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="rmdir"
	KEY="delete"
	SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80705-7
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rmdir
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit rmdir tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80705-7
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rmdir
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for rmdir for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rmdir
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rmdir in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rmdir
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rmdir in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80705-7
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rmdir
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for rmdir for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rmdir
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rmdir in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - rmdir
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of rmdir in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80705-7
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_rmdir
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit rmdir  oval:ssg-test_32bit_ardm_rmdir_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_rmdir_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit rmdir  oval:ssg-test_64bit_ardm_rmdir_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_rmdir_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit rmdir  oval:ssg-test_32bit_ardm_rmdir_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_rmdir_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit rmdir  oval:ssg-test_64bit_ardm_rmdir_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_rmdir_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+rmdir[\s]+|([\s]+|[,])rmdir([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects File Deletion Events by User - unlinkatxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlinkat mediumCCE-80707-3

Ensure auditd Collects File Deletion Events by User - unlinkat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlinkat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_file_deletion_events_unlinkat:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80707-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.4, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.1.1, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.MA-2, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000467-GPOS-00211, SRG-OS-000468-GPOS-00212
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
anssiR73
cis5.2.3.13
pcidss410.2.1.7, 10.2.1, 10.2
stigidRHEL-08-030361
stigrefSV-230439r1017243_rule
Description
At a minimum, the audit system should collect file deletion events for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S unlinkat -F auid>=1000 -F auid!=unset -F key=delete
Rationale
Auditing file deletions will create an audit trail for files that are removed from the system. The audit trail could aid in system troubleshooting, as well as, detecting malicious processes that attempt to delete log files to conceal their presence.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="unlinkat"
	KEY="delete"
	SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80707-3
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_unlinkat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit unlinkat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80707-3
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_unlinkat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for unlinkat for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - unlinkat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of unlinkat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - unlinkat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of unlinkat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80707-3
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_unlinkat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for unlinkat for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - unlinkat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of unlinkat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/delete.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/delete.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - unlinkat
      syscall_grouping:
      - unlink
      - unlinkat
      - rename
      - renameat
      - renameat2
      - rmdir

  - name: Check existence of unlinkat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=delete
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80707-3
  - DISA-STIG-RHEL-08-030361
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_file_deletion_events_unlinkat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit unlinkat  oval:ssg-test_32bit_ardm_unlinkat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_unlinkat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit unlinkat  oval:ssg-test_64bit_ardm_unlinkat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_unlinkat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit unlinkat  oval:ssg-test_32bit_ardm_unlinkat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_unlinkat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit unlinkat  oval:ssg-test_64bit_ardm_unlinkat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_unlinkat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+unlinkat[\s]+|([\s]+|[,])unlinkat([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Unsuccessful Access Attempts to Files - creatxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_creat mediumCCE-80751-1

Record Unsuccessful Access Attempts to Files - creat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_creat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_creat:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80751-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.7
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="creat"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit creat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for creat EACCES for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for creat EACCES for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for creat EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for creat EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - creat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of creat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80751-1
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_creat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_creat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_creat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_creat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_creat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_creat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_creat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_creat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_creat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_creat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_creat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_creat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_creat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_creat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_creat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_creat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_creat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+creat[\s]+|([\s]+|[,])creat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
/etc/audit/audit.rules1
Record Unsuccessful Access Attempts to Files - ftruncatexccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_ftruncate mediumCCE-80752-9

Record Unsuccessful Access Attempts to Files - ftruncate

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_ftruncate
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_ftruncate:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80752-9

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.7
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="ftruncate"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit ftruncate tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for ftruncate EACCES for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for ftruncate EACCES for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for ftruncate EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for ftruncate EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - ftruncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of ftruncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80752-9
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_ftruncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_ftruncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_ftruncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_ftruncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_ftruncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_ftruncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_ftruncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_ftruncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_ftruncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_ftruncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_ftruncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_ftruncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_ftruncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_ftruncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_ftruncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_ftruncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_ftruncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+ftruncate[\s]+|([\s]+|[,])ftruncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1
Record Unsuccessful Access Attempts to Files - openxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_open mediumCCE-80753-7

Record Unsuccessful Access Attempts to Files - open

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_open
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_open:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80753-7

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.7
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S open -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S open -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="open"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit open tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open EACCES for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open EACCES for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - not ( ansible_architecture == "aarch64" )
  - audit_arch == "b64"
  tags:
  - CCE-80753-7
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_open_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_open_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_open_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_open_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_open_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_open_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_open_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_open_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_open_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_open_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_open_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_open_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_open_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_open_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_open_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_open_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open[\s]+|([\s]+|[,])open([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1
Record Unsuccessful Access Attempts to Files - open_by_handle_atxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_open_by_handle_at mediumCCE-80755-2

Record Unsuccessful Access Attempts to Files - open_by_handle_at

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_open_by_handle_at
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_open_by_handle_at:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80755-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="open_by_handle_at"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit open_by_handle_at tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open_by_handle_at EACCES for 32bit
    platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open_by_handle_at EACCES for 64bit
    platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open_by_handle_at EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for open_by_handle_at EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - open_by_handle_at
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of open_by_handle_at in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80755-2
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_open_by_handle_at
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_open_by_handle_at_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_open_by_handle_at_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_open_by_handle_at_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_open_by_handle_at_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_open_by_handle_at_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_open_by_handle_at_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_open_by_handle_at_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_open_by_handle_at_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_open_by_handle_at_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_open_by_handle_at_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_open_by_handle_at_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_open_by_handle_at_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_open_by_handle_at_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_open_by_handle_at_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_open_by_handle_at_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_open_by_handle_at_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+open_by_handle_at[\s]+|([\s]+|[,])open_by_handle_at([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1
Record Unsuccessful Access Attempts to Files - openatxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_openat mediumCCE-80754-5

Record Unsuccessful Access Attempts to Files - openat

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_openat
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_openat:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80754-5

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.7
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S openat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S openat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="openat"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit openat tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for openat EACCES for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for openat EACCES for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for openat EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for openat EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - openat
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of openat in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80754-5
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_openat
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_openat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_openat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_openat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_openat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_openat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_openat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_openat_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_openat_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_openat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_openat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_openat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_openat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_openat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_openat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_openat_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_openat_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+openat[\s]+|([\s]+|[,])openat([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1
Record Unsuccessful Access Attempts to Files - truncatexccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_truncate mediumCCE-80756-0

Record Unsuccessful Access Attempts to Files - truncate

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification_truncate
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_unsuccessful_file_modification_truncate:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80756-0

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.4, Req-10.2.1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000064-GPOS-00033, SRG-OS-000458-GPOS-00203, SRG-OS-000461-GPOS-00205
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.7
stigidRHEL-08-030420
stigrefSV-230449r1017249_rule
Description
At a minimum, the audit system should collect unauthorized file accesses for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S truncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S truncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
Rationale
Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL="truncate"
KEY="access"
SYSCALL_GROUPING="creat ftruncate truncate open openat open_by_handle_at"

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EACCES"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS="-F exit=-EPERM"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit truncate tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for truncate EACCES for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for truncate EACCES for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EACCES -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EACCES -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EACCES
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for truncate EPERM for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for truncate EPERM for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/access.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/access.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - truncate
      syscall_grouping:
      - creat
      - ftruncate
      - truncate
      - open
      - openat
      - open_by_handle_at

  - name: Check existence of truncate in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F exit=-EPERM -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F exit=-EPERM -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F exit=-EPERM
        -F auid>=1000 -F auid!=unset -F key=access
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80756-0
  - DISA-STIG-RHEL-08-030420
  - NIST-800-171-3.1.7
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.1
  - PCI-DSS-Req-10.2.4
  - audit_rules_unsuccessful_file_modification_truncate
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_truncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_truncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_truncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_truncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_truncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_truncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit augenrules 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_truncate_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_truncate_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit file eacces  oval:ssg-test_32bit_arufm_eacces_truncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eacces_truncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 32-bit file eperm  oval:ssg-test_32bit_arufm_eperm_truncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_arufm_eperm_truncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit file eacces  oval:ssg-test_64bit_arufm_eacces_truncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eacces_truncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EACCES)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1

audit auditctl 64-bit file eperm  oval:ssg-test_64bit_arufm_eperm_truncate_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_arufm_eperm_truncate_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*
^[\s]*-a[\s]+always,exit[\s]+(?:-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+truncate[\s]+|([\s]+|[,])truncate([\s]+|[,])))(?:(?!-F[\s]+a\d&).)*(?:-F\s+exit=-EPERM)[\s]+(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295)[\s]+)(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
/etc/audit/audit.rules1
Ensure auditd Collects Information on Kernel Module Unloading - delete_modulexccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_delete mediumCCE-80711-5

Ensure auditd Collects Information on Kernel Module Unloading - delete_module

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_delete
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_kernel_module_loading_delete:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80711-5

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000471-GPOS-00216, SRG-OS-000477-GPOS-00222
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000504-CTR-001280
anssiR73
cis5.2.3.19
stigidRHEL-08-030390
stigrefSV-230446r1017245_rule
Description
To capture kernel module unloading events, use following line, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S delete_module -F auid>=1000 -F auid!=unset -F key=modules
Place to add the line depends on a way auditd daemon is configured. If it is configured to use the augenrules program (the default), add the line to a file with suffix .rules in the directory /etc/audit/rules.d. If the auditd daemon is configured to use the auditctl utility, add the line to file /etc/audit/audit.rules.
Rationale
The removal of kernel modules can be used to alter the behavior of the kernel and potentially introduce malicious code into kernel space. It is important to have an audit trail of modules that have been introduced into the kernel.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit and 64-bit kernel syscall numbers not always line up =>
#       it's required on a 64-bit system to check also for the presence
#       of 32-bit's equivalent of the corresponding rule.
#       (See `man 7 audit.rules` for details )
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	
	SYSCALL="delete_module"
	KEY="modules"
	SYSCALL_GROUPING="delete_module"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80711-5
  - DISA-STIG-RHEL-08-030390
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_delete
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Set architecture for audit delete_module tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80711-5
  - DISA-STIG-RHEL-08-030390
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_delete
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for delete_module for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - delete_module
      syscall_grouping: []

  - name: Check existence of delete_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - delete_module
      syscall_grouping: []

  - name: Check existence of delete_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80711-5
  - DISA-STIG-RHEL-08-030390
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_delete
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for delete_module for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - delete_module
      syscall_grouping: []

  - name: Check existence of delete_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - delete_module
      syscall_grouping: []

  - name: Check existence of delete_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80711-5
  - DISA-STIG-RHEL-08-030390
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_delete
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20delete_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20delete_module%20-k%20module-change%0A
        mode: 0600
        path: /etc/audit/rules.d/75-kernel-module-loading-delete.rules
        overwrite: true
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit delete_module  oval:ssg-test_32bit_ardm_delete_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_delete_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit delete_module  oval:ssg-test_64bit_ardm_delete_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_delete_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit delete_module  oval:ssg-test_32bit_ardm_delete_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_delete_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit delete_module  oval:ssg-test_64bit_ardm_delete_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_delete_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+delete_module[\s]+|([\s]+|[,])delete_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_modulexccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_finit mediumCCE-80712-3

Ensure auditd Collects Information on Kernel Module Loading and Unloading - finit_module

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_finit
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_kernel_module_loading_finit:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80712-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000471-GPOS-00216, SRG-OS-000477-GPOS-00222
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000504-CTR-001280
anssiR73
cis5.2.3.19
stigidRHEL-08-030360
stigrefSV-230438r1017241_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d to capture kernel module loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system:
-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules file in order to capture kernel module loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system:
-a always,exit -F arch=ARCH -S finit_module -F auid>=1000 -F auid!=unset -F key=modules
Rationale
The addition/removal of kernel modules can be used to alter the behavior of the kernel and potentially introduce malicious code into kernel space. It is important to have an audit trail of modules that have been introduced into the kernel.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit and 64-bit kernel syscall numbers not always line up =>
#       it's required on a 64-bit system to check also for the presence
#       of 32-bit's equivalent of the corresponding rule.
#       (See `man 7 audit.rules` for details )
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	
	SYSCALL="finit_module"
	KEY="modules"
	SYSCALL_GROUPING="init_module finit_module"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80712-3
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_finit
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Set architecture for audit finit_module tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80712-3
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_finit
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for finit_module for x86 platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - finit_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of finit_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - finit_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of finit_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80712-3
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_finit
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for finit_module for x86_64 platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - finit_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of finit_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - finit_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of finit_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80712-3
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_finit
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20finit_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20finit_module%20-k%20module-change%0A
        mode: 0600
        path: /etc/audit/rules.d/75-kernel-module-loading-finit.rules
        overwrite: true
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit finit_module  oval:ssg-test_32bit_ardm_finit_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_finit_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit finit_module  oval:ssg-test_64bit_ardm_finit_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_finit_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit finit_module  oval:ssg-test_32bit_ardm_finit_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_finit_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit finit_module  oval:ssg-test_64bit_ardm_finit_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_finit_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+finit_module[\s]+|([\s]+|[,])finit_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on Kernel Module Loading - init_modulexccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_init mediumCCE-80713-1

Ensure auditd Collects Information on Kernel Module Loading - init_module

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading_init
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_kernel_module_loading_init:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80713-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000471-GPOS-00216, SRG-OS-000477-GPOS-00222
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000504-CTR-001280
anssiR73
cis5.2.3.19
stigidRHEL-08-030360
stigrefSV-230438r1017241_rule
Description
To capture kernel module loading events, use following line, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S init_module -F auid>=1000 -F auid!=unset -F key=modules
Place to add the line depends on a way auditd daemon is configured. If it is configured to use the augenrules program (the default), add the line to a file with suffix .rules in the directory /etc/audit/rules.d. If the auditd daemon is configured to use the auditctl utility, add the line to file /etc/audit/audit.rules.
Rationale
The addition of kernel modules can be used to alter the behavior of the kernel and potentially introduce malicious code into kernel space. It is important to have an audit trail of modules that have been introduced into the kernel.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit and 64-bit kernel syscall numbers not always line up =>
#       it's required on a 64-bit system to check also for the presence
#       of 32-bit's equivalent of the corresponding rule.
#       (See `man 7 audit.rules` for details )
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	
	SYSCALL="init_module"
	KEY="modules"
	SYSCALL_GROUPING="init_module finit_module"
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80713-1
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_init
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Set architecture for audit init_module tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80713-1
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_init
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for init_module for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - init_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of init_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - init_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of init_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80713-1
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_init
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

- name: Perform remediation of Audit rules for init_module for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - init_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of init_module in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/module-change.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/module-change.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - init_module
      syscall_grouping:
      - init_module
      - finit_module

  - name: Check existence of init_module in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=module-change
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80713-1
  - DISA-STIG-RHEL-08-030360
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - audit_rules_kernel_module_loading_init
  - configure_strategy
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20init_module%20-k%20module-change%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20init_module%20-k%20module-change%0A
        mode: 0600
        path: /etc/audit/rules.d/75-kernel-module-loading-init.rules
        overwrite: true
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit init_module  oval:ssg-test_32bit_ardm_init_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_init_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit init_module  oval:ssg-test_64bit_ardm_init_module_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_init_module_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit init_module  oval:ssg-test_32bit_ardm_init_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_init_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit init_module  oval:ssg-test_64bit_ardm_init_module_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_init_module_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+init_module[\s]+|([\s]+|[,])init_module([\s]+|[,]))).*(?:-F\s+auid>=1000[\s]+)(?:-F\s+auid!=(unset|4294967295))\s+(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - chagexccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_chage mediumCCE-80725-5

Ensure auditd Collects Information on the Use of Privileged Commands - chage

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_chage
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_chage:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80725-5

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000468-GPOS-00212, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235, SRG-APP-000501-CTR-001265, SRG-APP-000502-CTR-001270
stigidRHEL-08-030250
stigrefSV-230418r1017220_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/chage -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80725-5
  - DISA-STIG-RHEL-08-030250
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_chage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - chage
    - Perform remediation of Audit rules for /usr/bin/chage
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chage -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80725-5
  - DISA-STIG-RHEL-08-030250
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_chage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules chage  oval:ssg-test_audit_rules_privileged_commands_chage_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_chage_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl chage  oval:ssg-test_audit_rules_privileged_commands_chage_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_chage_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chage(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - chshxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_chsh mediumCCE-80726-3

Ensure auditd Collects Information on the Use of Privileged Commands - chsh

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_chsh
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_chsh:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80726-3

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030410
stigrefSV-230448r1017247_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/chsh -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80726-3
  - DISA-STIG-RHEL-08-030410
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_chsh
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - chsh
    - Perform remediation of Audit rules for /usr/bin/chsh
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/chsh -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80726-3
  - DISA-STIG-RHEL-08-030410
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_chsh
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules chsh  oval:ssg-test_audit_rules_privileged_commands_chsh_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_chsh_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl chsh  oval:ssg-test_audit_rules_privileged_commands_chsh_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_chsh_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/chsh(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - crontabxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_crontab mediumCCE-80727-1

Ensure auditd Collects Information on the Use of Privileged Commands - crontab

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_crontab
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_crontab:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80727-1

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030400
stigrefSV-230447r1017246_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/crontab -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80727-1
  - DISA-STIG-RHEL-08-030400
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_crontab
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - crontab
    - Perform remediation of Audit rules for /usr/bin/crontab
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/crontab -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80727-1
  - DISA-STIG-RHEL-08-030400
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_crontab
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules crontab  oval:ssg-test_audit_rules_privileged_commands_crontab_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_crontab_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl crontab  oval:ssg-test_audit_rules_privileged_commands_crontab_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_crontab_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/crontab(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - gpasswdxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_gpasswd mediumCCE-80728-9

Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_gpasswd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_gpasswd:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80728-9

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030370
stigrefSV-230444r1017244_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/gpasswd -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80728-9
  - DISA-STIG-RHEL-08-030370
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_gpasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - gpasswd
    - Perform remediation of Audit rules for /usr/bin/gpasswd
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/gpasswd -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80728-9
  - DISA-STIG-RHEL-08-030370
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_gpasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules gpasswd  oval:ssg-test_audit_rules_privileged_commands_gpasswd_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_gpasswd_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl gpasswd  oval:ssg-test_audit_rules_privileged_commands_gpasswd_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_gpasswd_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/gpasswd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - kmodxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_kmod mediumCCE-89455-0

Ensure auditd Collects Information on the Use of Privileged Commands - kmod

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_kmod
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_kmod:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-89455-0

References:
nistAU-3, AU-3.1, AU-12(a), AU-12.1(ii), AU-12.1(iv)AU-12(c), MA-4(1)(a)
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000471-GPOS-00216, SRG-OS-000477-GPOS-00222
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000504-CTR-001280
anssiR73
cis5.2.3.19
stigidRHEL-08-030580
stigrefSV-230465r1017257_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. Audit records can be generated from various components within the information system (e.g., module or policy filter).

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/kmod -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-89455-0
  - DISA-STIG-RHEL-08-030580
  - NIST-800-53-AU-12(a)
  - NIST-800-53-AU-12.1(ii)
  - NIST-800-53-AU-12.1(iv)AU-12(c)
  - NIST-800-53-AU-3
  - NIST-800-53-AU-3.1
  - NIST-800-53-MA-4(1)(a)
  - audit_rules_privileged_commands_kmod
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - kmod
    - Perform remediation of Audit rules for /usr/bin/kmod
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/kmod -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-89455-0
  - DISA-STIG-RHEL-08-030580
  - NIST-800-53-AU-12(a)
  - NIST-800-53-AU-12.1(ii)
  - NIST-800-53-AU-12.1(iv)AU-12(c)
  - NIST-800-53-AU-3
  - NIST-800-53-AU-3.1
  - NIST-800-53-MA-4(1)(a)
  - audit_rules_privileged_commands_kmod
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules kmod  oval:ssg-test_audit_rules_privileged_commands_kmod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_kmod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl kmod  oval:ssg-test_audit_rules_privileged_commands_kmod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_kmod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/kmod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - mountxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_mount mediumCCE-80989-7

Ensure auditd Collects Information on the Use of Privileged Commands - mount

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_mount
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_mount:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80989-7

References:
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085
stigidRHEL-08-030300
stigrefSV-230423r1017224_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/mount -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80989-7
  - DISA-STIG-RHEL-08-030300
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_mount
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - mount
    - Perform remediation of Audit rules for /usr/bin/mount
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/mount -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80989-7
  - DISA-STIG-RHEL-08-030300
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_mount
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules mount  oval:ssg-test_audit_rules_privileged_commands_mount_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_mount_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl mount  oval:ssg-test_audit_rules_privileged_commands_mount_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_mount_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/mount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - newgrpxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_newgrp mediumCCE-80729-7

Ensure auditd Collects Information on the Use of Privileged Commands - newgrp

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_newgrp
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_newgrp:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80729-7

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030350
stigrefSV-230437r1017238_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/newgrp -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80729-7
  - DISA-STIG-RHEL-08-030350
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_newgrp
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - newgrp
    - Perform remediation of Audit rules for /usr/bin/newgrp
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/newgrp -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80729-7
  - DISA-STIG-RHEL-08-030350
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_newgrp
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules newgrp  oval:ssg-test_audit_rules_privileged_commands_newgrp_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_newgrp_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl newgrp  oval:ssg-test_audit_rules_privileged_commands_newgrp_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_newgrp_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/newgrp(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_checkxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_pam_timestamp_check mediumCCE-80730-5

Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_pam_timestamp_check
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_pam_timestamp_check:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80730-5

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030340
stigrefSV-230436r1017237_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/pam_timestamp_check -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80730-5
  - DISA-STIG-RHEL-08-030340
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_pam_timestamp_check
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - pam_timestamp_check
    - Perform remediation of Audit rules for /usr/sbin/pam_timestamp_check
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset
        (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check
        -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000 -F auid!=unset
        (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/pam_timestamp_check -F perm=x -F auid>=1000
        -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/pam_timestamp_check
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80730-5
  - DISA-STIG-RHEL-08-030340
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_pam_timestamp_check
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules pam_timestamp_check  oval:ssg-test_audit_rules_privileged_commands_pam_timestamp_check_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_pam_timestamp_check_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl pam_timestamp_check  oval:ssg-test_audit_rules_privileged_commands_pam_timestamp_check_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_pam_timestamp_check_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/pam_timestamp_check(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - passwdxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_passwd mediumCCE-80731-3

Ensure auditd Collects Information on the Use of Privileged Commands - passwd

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_passwd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_passwd:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80731-3

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030290
stigrefSV-230422r1017223_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/passwd -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80731-3
  - DISA-STIG-RHEL-08-030290
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - passwd
    - Perform remediation of Audit rules for /usr/bin/passwd
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/passwd -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80731-3
  - DISA-STIG-RHEL-08-030290
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules passwd  oval:ssg-test_audit_rules_privileged_commands_passwd_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_passwd_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl passwd  oval:ssg-test_audit_rules_privileged_commands_passwd_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_passwd_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/passwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - postdropxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_postdrop mediumCCE-80732-1

Ensure auditd Collects Information on the Use of Privileged Commands - postdrop

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_postdrop
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_postdrop:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80732-1

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030311
stigrefSV-230427r1017228_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/postdrop -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80732-1
  - DISA-STIG-RHEL-08-030311
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_postdrop
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - postdrop
    - Perform remediation of Audit rules for /usr/sbin/postdrop
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postdrop -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80732-1
  - DISA-STIG-RHEL-08-030311
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_postdrop
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules postdrop  oval:ssg-test_audit_rules_privileged_commands_postdrop_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_postdrop_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl postdrop  oval:ssg-test_audit_rules_privileged_commands_postdrop_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_postdrop_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postdrop(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - postqueuexccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_postqueue mediumCCE-80733-9

Ensure auditd Collects Information on the Use of Privileged Commands - postqueue

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_postqueue
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_postqueue:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80733-9

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030312
stigrefSV-230428r1017229_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/postqueue -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80733-9
  - DISA-STIG-RHEL-08-030312
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_postqueue
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - postqueue
    - Perform remediation of Audit rules for /usr/sbin/postqueue
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/postqueue -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80733-9
  - DISA-STIG-RHEL-08-030312
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_postqueue
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules postqueue  oval:ssg-test_audit_rules_privileged_commands_postqueue_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_postqueue_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl postqueue  oval:ssg-test_audit_rules_privileged_commands_postqueue_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_postqueue_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/postqueue(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Any Attempts to Run ssh-agentxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_ssh_agent mediumCCE-85944-7

Record Any Attempts to Run ssh-agent

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_ssh_agent
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_ssh_agent:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-85944-7

References:
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030280
stigrefSV-230421r1017222_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. Audit records can be generated from various components within the information system (e.g., module or policy filter).

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/ssh-agent -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-85944-7
  - DISA-STIG-RHEL-08-030280
  - audit_rules_privileged_commands_ssh_agent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Any Attempts to Run ssh-agent - Perform remediation of Audit rules
    for /usr/bin/ssh-agent
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/ssh-agent -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-85944-7
  - DISA-STIG-RHEL-08-030280
  - audit_rules_privileged_commands_ssh_agent
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules ssh_agent  oval:ssg-test_audit_rules_privileged_commands_ssh_agent_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_ssh_agent_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl ssh_agent  oval:ssg-test_audit_rules_privileged_commands_ssh_agent_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_ssh_agent_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/ssh-agent(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysignxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_ssh_keysign mediumCCE-80735-4

Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_ssh_keysign
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_ssh_keysign:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80735-4

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030320
stigrefSV-230434r1017235_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/libexec/openssh/ssh-keysign -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80735-4
  - DISA-STIG-RHEL-08-030320
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_ssh_keysign
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - ssh-keysign
    - Perform remediation of Audit rules for /usr/libexec/openssh/ssh-keysign
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset
        (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign
        -F perm=x -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=unset
        (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/libexec/openssh/ssh-keysign -F perm=x -F auid>=1000
        -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/libexec/openssh/ssh-keysign
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80735-4
  - DISA-STIG-RHEL-08-030320
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_ssh_keysign
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules ssh_keysign  oval:ssg-test_audit_rules_privileged_commands_ssh_keysign_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_ssh_keysign_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl ssh_keysign  oval:ssg-test_audit_rules_privileged_commands_ssh_keysign_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_ssh_keysign_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/libexec\/openssh\/ssh-keysign(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - suxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_su mediumCCE-80736-2

Ensure auditd Collects Information on the Use of Privileged Commands - su

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_su
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_su:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80736-2

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000755-GPOS-00220
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
stigidRHEL-08-030190
stigrefSV-230412r1017218_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/su -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80736-2
  - DISA-STIG-RHEL-08-030190
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_su
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - su
    - Perform remediation of Audit rules for /usr/bin/su
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000
        -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/su -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80736-2
  - DISA-STIG-RHEL-08-030190
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_su
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules su  oval:ssg-test_audit_rules_privileged_commands_su_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_su_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl su  oval:ssg-test_audit_rules_privileged_commands_su_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_su_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/su(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - sudoxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_sudo mediumCCE-80737-0

Ensure auditd Collects Information on the Use of Privileged Commands - sudo

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_sudo
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_sudo:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80737-0

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210, SRG-OS-000755-GPOS-00220
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
anssiR33
stigidRHEL-08-030550
stigrefSV-230462r1017254_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/sudo -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80737-0
  - DISA-STIG-RHEL-08-030550
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_sudo
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - sudo
    - Perform remediation of Audit rules for /usr/bin/sudo
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/sudo -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80737-0
  - DISA-STIG-RHEL-08-030550
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_sudo
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules sudo  oval:ssg-test_audit_rules_privileged_commands_sudo_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_sudo_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl sudo  oval:ssg-test_audit_rules_privileged_commands_sudo_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_sudo_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/sudo(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - umountxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_umount mediumCCE-80739-6

Ensure auditd Collects Information on the Use of Privileged Commands - umount

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_umount
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_umount:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80739-6

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085
stigidRHEL-08-030301
stigrefSV-230424r1017225_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/bin/umount -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80739-6
  - DISA-STIG-RHEL-08-030301
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_umount
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - umount
    - Perform remediation of Audit rules for /usr/bin/umount
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F
        auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/bin/umount -F perm=x
        -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80739-6
  - DISA-STIG-RHEL-08-030301
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_umount
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules umount  oval:ssg-test_audit_rules_privileged_commands_umount_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_umount_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl umount  oval:ssg-test_audit_rules_privileged_commands_umount_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_umount_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/bin\/umount(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwdxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_unix_chkpwd mediumCCE-80740-4

Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_unix_chkpwd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_unix_chkpwd:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80740-4

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3, CIP-007-3 R6.5
nistAC-2(4), AU-2(d), AU-3, AU-3.1, AU-12(a), AU-12(c), AU-12.1(ii), AU-12.1(iv), AC-6(9), CM-6(a), MA-4(1)(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000029-CTR-000085, SRG-APP-000495-CTR-001235
stigidRHEL-08-030317
stigrefSV-230433r1017234_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/unix_chkpwd -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80740-4
  - DISA-STIG-RHEL-08-030317
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(a)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-12.1(ii)
  - NIST-800-53-AU-12.1(iv)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-AU-3
  - NIST-800-53-AU-3.1
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(1)(a)
  - audit_rules_privileged_commands_unix_chkpwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - unix_chkpwd
    - Perform remediation of Audit rules for /usr/sbin/unix_chkpwd
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_chkpwd
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80740-4
  - DISA-STIG-RHEL-08-030317
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(a)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-12.1(ii)
  - NIST-800-53-AU-12.1(iv)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-AU-3
  - NIST-800-53-AU-3.1
  - NIST-800-53-CM-6(a)
  - NIST-800-53-MA-4(1)(a)
  - audit_rules_privileged_commands_unix_chkpwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules unix_chkpwd  oval:ssg-test_audit_rules_privileged_commands_unix_chkpwd_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_unix_chkpwd_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl unix_chkpwd  oval:ssg-test_audit_rules_privileged_commands_unix_chkpwd_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_unix_chkpwd_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_chkpwd(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - unix_updatexccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_unix_update mediumCCE-89480-8

Ensure auditd Collects Information on the Use of Privileged Commands - unix_update

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_unix_update
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_unix_update:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-89480-8

References:
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030310
stigrefSV-230426r1017227_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/unix_update -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-89480-8
  - DISA-STIG-RHEL-08-030310
  - audit_rules_privileged_commands_unix_update
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - unix_update
    - Perform remediation of Audit rules for /usr/sbin/unix_update
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/unix_update -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/unix_update
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-89480-8
  - DISA-STIG-RHEL-08-030310
  - audit_rules_privileged_commands_unix_update
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules unix_update  oval:ssg-test_audit_rules_privileged_commands_unix_update_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_unix_update_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl unix_update  oval:ssg-test_audit_rules_privileged_commands_unix_update_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_unix_update_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/unix_update(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - userhelperxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_userhelper mediumCCE-80741-2

Ensure auditd Collects Information on the Use of Privileged Commands - userhelper

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_userhelper
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_userhelper:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80741-2

References:
cis-csc1, 12, 13, 14, 15, 16, 2, 3, 5, 6, 7, 8, 9
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, BAI03.05, DSS01.03, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 6.2
iso27001-2013A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.14.2.7, A.15.2.1, A.15.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.PT-1
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
stigidRHEL-08-030315
stigrefSV-230431r1017232_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/userhelper -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80741-2
  - DISA-STIG-RHEL-08-030315
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_userhelper
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - userhelper
    - Perform remediation of Audit rules for /usr/sbin/userhelper
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/userhelper
        -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80741-2
  - DISA-STIG-RHEL-08-030315
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - audit_rules_privileged_commands_userhelper
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules userhelper  oval:ssg-test_audit_rules_privileged_commands_userhelper_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_userhelper_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl userhelper  oval:ssg-test_audit_rules_privileged_commands_userhelper_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_userhelper_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/userhelper(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Information on the Use of Privileged Commands - usermodxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_usermod mediumCCE-86027-0

Ensure auditd Collects Information on the Use of Privileged Commands - usermod

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_privileged_commands_usermod
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_privileged_commands_usermod:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-86027-0

References:
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000466-GPOS-00210
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255
cis5.2.3.18
stigidRHEL-08-030560
stigrefSV-230463r1017255_rule
Description
At a minimum, the audit system should collect the execution of privileged commands for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add a line of the following form to a file with suffix .rules in the directory /etc/audit/rules.d:
-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add a line of the following form to /etc/audit/audit.rules:
-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider and advanced persistent threats.

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Retrieve hardware architecture of the underlying system
OTHER_FILTERS="-F path=/usr/sbin/usermod -F perm=x"
AUID_FILTERS="-F auid>=1000 -F auid!=unset"
SYSCALL=""
KEY="privileged"
SYSCALL_GROUPING=""


ACTION_ARCH_FILTERS="-a always,exit"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86027-0
  - DISA-STIG-RHEL-08-030560
  - audit_rules_privileged_commands_usermod
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Information on the Use of Privileged Commands - usermod
    - Perform remediation of Audit rules for /usr/sbin/usermod
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/privileged.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/privileged.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x
        -F auid>=1000 -F auid!=unset (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls: []
      syscall_grouping: []

  - name: Check existence of  in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit(( -S |,)\w+)*(( -S |,){{ item }})+(( -S |,)\w+)* -F
        path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit)(?=.*(?:(?:-S |,)(?:{{ syscalls_found | join("|") }}))\b)((?:(
        -S |,)\w+)+)( -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset
        (?:-k |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit{{ syscalls | join(',') }} -F path=/usr/sbin/usermod -F
        perm=x -F auid>=1000 -F auid!=unset -F key=privileged
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86027-0
  - DISA-STIG-RHEL-08-030560
  - audit_rules_privileged_commands_usermod
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules usermod  oval:ssg-test_audit_rules_privileged_commands_usermod_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_usermod_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl usermod  oval:ssg-test_audit_rules_privileged_commands_usermod_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_privileged_commands_usermod_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+path=\/usr\/sbin\/usermod(?:[\s]+-F[\s]+perm=x)[\s]+-F[\s]+auid>=1000[\s]+-F[\s]+auid!=(?:4294967295|unset|-1)[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/xccdf_org.ssgproject.content_rule_audit_rules_etc_cron_d mediumCCE-86606-1

Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_etc_cron_d
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_etc_cron_d:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-86606-1

References:
os-srgSRG-OS-000471-GPOS-00215
stigidRHEL-08-030655
stigrefSV-274877r1106148_rule
Description
At a minimum, the audit system should collect administrator actions for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/cron.d/ -p wa -k cronjobs
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/cron.d/ -p wa -k cronjobs
Rationale
The actions taken by system administrators should be audited to keep a record of what was executed on the system, as well as, for accountability purposes. Editing the sudoers file may be sign of an attacker trying to establish persistent methods to a system, auditing the editing of the sudoers files mitigates this risk.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/cron.d/" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/cron.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/cron.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/cron.d/ -p wa -k actions" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/cron.d/" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/actions.rules"
    # If the actions.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/cron.d/" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/cron.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/cron.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/cron.d/ -p wa -k actions" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Check if watch
    rule for /etc/cron.d/ already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/cron.d/\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Search /etc/audit/rules.d
    for other rules with specified key actions
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)actions$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Use /etc/audit/rules.d/actions.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/actions.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Use matched file
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Add watch rule
    for /etc/cron.d/ in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/cron.d/ -p wa -k actions
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Check if watch
    rule for /etc/cron.d/ already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/cron.d/\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /etc/cron.d/ - Add watch rule
    for /etc/cron.d/ in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/cron.d/ -p wa -k actions
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-86606-1
  - DISA-STIG-RHEL-08-030655
  - audit_rules_etc_cron_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules cron_d  oval:ssg-test_audit_rules_etc_cron_d_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_etc_cron_d_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/cron.d\/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl cron_d  oval:ssg-test_audit_rules_etc_cron_d_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_etc_cron_d_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/cron.d\/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Make the auditd Configuration Immutablexccdf_org.ssgproject.content_rule_audit_rules_immutable mediumCCE-80708-1

Make the auditd Configuration Immutable

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_immutable
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_immutable:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80708-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.3.1, 3.4.3
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.310(a)(2)(iv), 164.312(d), 164.310(d)(2)(iii), 164.312(b), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistAC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, ID.SC-4, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5.2
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029
app-srg-ctrSRG-APP-000119-CTR-000245, SRG-APP-000120-CTR-000250
anssiR73
cis5.2.3.20
pcidss410.3.2, 10.3
stigidRHEL-08-030121
stigrefSV-230402r1017208_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d in order to make the auditd configuration immutable:
-e 2
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file in order to make the auditd configuration immutable:
-e 2
With this setting, a reboot will be required to change any audit rules.
Rationale
Making the audit configuration immutable prevents accidental as well as malicious modification of the audit rules, although it may be problematic if legitimate changes are needed during system operation.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Traverse all of:
#
# /etc/audit/audit.rules,			(for auditctl case)
# /etc/audit/rules.d/*.rules			(for augenrules case)
#
# files to check if '-e .*' setting is present in that '*.rules' file already.
# If found, delete such occurrence since auditctl(8) manual page instructs the
# '-e 2' rule should be placed as the last rule in the configuration
find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-e[[:space:]]\+.*/d' {} ';'

# Append '-e 2' requirement at the end of both:
# * /etc/audit/audit.rules file 		(for auditctl case)
# * /etc/audit/rules.d/immutable.rules		(for augenrules case)

for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules"
do
	echo '' >> $AUDIT_FILE
	echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_FILE
	echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_FILE
	echo '-e 2' >> $AUDIT_FILE
	chmod o-rwx $AUDIT_FILE
	chmod g-rwx $AUDIT_FILE
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80708-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030121
  - NIST-800-171-3.3.1
  - NIST-800-171-3.4.3
  - NIST-800-53-AC-6(9)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.2
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.2
  - audit_rules_immutable
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Collect all files from /etc/audit/rules.d with .rules extension
  ansible.builtin.find:
    paths: /etc/audit/rules.d/
    patterns: '*.rules'
  register: find_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80708-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030121
  - NIST-800-171-3.3.1
  - NIST-800-171-3.4.3
  - NIST-800-53-AC-6(9)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.2
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.2
  - audit_rules_immutable
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Remove the -e option from all Audit config files
  ansible.builtin.lineinfile:
    path: '{{ item }}'
    regexp: ^\s*(?:-e)\s+.*$
    state: absent
  loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules'']
    }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80708-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030121
  - NIST-800-171-3.3.1
  - NIST-800-171-3.4.3
  - NIST-800-53-AC-6(9)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.2
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.2
  - audit_rules_immutable
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Add Audit -e option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    path: '{{ item }}'
    create: true
    line: -e 2
    mode: g-rwx,o-rwx
  loop:
  - /etc/audit/audit.rules
  - /etc/audit/rules.d/immutable.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80708-1
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030121
  - NIST-800-171-3.3.1
  - NIST-800-171-3.4.3
  - NIST-800-53-AC-6(9)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.5.2
  - PCI-DSSv4-10.3
  - PCI-DSSv4-10.3.2
  - audit_rules_immutable
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,-e%202%0A
        mode: 0600
        path: /etc/audit/rules.d/90-immutable.rules
        overwrite: true
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules configuration locked  oval:ssg-test_ari_locked_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_ari_locked_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^\-e\s+2\s*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl configuration locked  oval:ssg-test_ari_locked_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_ari_locked_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^\-e\s+2\s*$1
Ensure auditd Collects Information on Exporting to Media (successful)xccdf_org.ssgproject.content_rule_audit_rules_media_export mediumCCE-80722-2

Ensure auditd Collects Information on Exporting to Media (successful)

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_media_export
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_media_export:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80722-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.7
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
app-srg-ctrSRG-APP-000495-CTR-001235
anssiR73
cis5.2.3.10
pcidss410.2.1.7, 10.2.1, 10.2
stigidRHEL-08-030302
stigrefSV-230425r1017226_rule
Description
At a minimum, the audit system should collect media exportation events for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following line to a file with suffix .rules in the directory /etc/audit/rules.d, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following line to /etc/audit/audit.rules file, setting ARCH to either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export
Rationale
The unauthorized exportation of data to external media could result in an information leak where classified information, Privacy Act information, and intellectual property could be lost. An audit trail should be created each time a filesystem is mounted to help identify and guard against information loss.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
	OTHER_FILTERS=""
	AUID_FILTERS="-F auid>=1000 -F auid!=unset"
	SYSCALL="mount"
	KEY="export"
	SYSCALL_GROUPING=""

	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80722-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030302
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_media_export
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Set architecture for audit mount tasks
  ansible.builtin.set_fact:
    audit_arch: b64
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ansible_architecture == "aarch64" or ansible_architecture == "ppc64" or ansible_architecture
    == "ppc64le" or ansible_architecture == "s390x" or ansible_architecture == "x86_64"
  tags:
  - CCE-80722-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030302
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_media_export
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for mount for 32bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - mount
      syscall_grouping: []

  - name: Check existence of mount in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/export.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/export.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=export
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - mount
      syscall_grouping: []

  - name: Check existence of mount in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b32(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b32)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b32 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=export
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80722-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030302
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_media_export
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy

- name: Perform remediation of Audit rules for mount for 64bit platform
  block:

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - mount
      syscall_grouping: []

  - name: Check existence of mount in /etc/audit/rules.d/
    ansible.builtin.find:
      paths: /etc/audit/rules.d
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: '*.rules'
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Reset syscalls found per file
    ansible.builtin.set_fact:
      syscalls_per_file: {}
      found_paths_dict: {}

  - name: Declare syscalls found per file
    ansible.builtin.set_fact: syscalls_per_file="{{ syscalls_per_file | combine( {item.files[0].path
      :[item.item] + syscalls_per_file.get(item.files[0].path, []) } ) }}"
    loop: '{{ find_command.results | selectattr(''matched'') | list }}'

  - name: Declare files where syscalls were found
    ansible.builtin.set_fact: found_paths="{{ find_command.results | map(attribute='files')
      | flatten | map(attribute='path') | list }}"

  - name: Count occurrences of syscalls in paths
    ansible.builtin.set_fact: found_paths_dict="{{ found_paths_dict | combine({ item:1+found_paths_dict.get(item,
      0) }) }}"
    loop: '{{ find_command.results | map(attribute=''files'') | flatten | map(attribute=''path'')
      | list }}'

  - name: Get path with most syscalls
    ansible.builtin.set_fact: audit_file="{{ (found_paths_dict | dict2items() | sort(attribute='value')
      | last).key }}"
    when: found_paths | length >= 1

  - name: No file with syscall found, set path to /etc/audit/rules.d/export.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/rules.d/export.rules"
    when: found_paths | length == 0

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_per_file[audit_file]
        | join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k
        |-F key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=export
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0

  - name: Declare list of syscalls
    ansible.builtin.set_fact:
      syscalls:
      - mount
      syscall_grouping: []

  - name: Check existence of mount in /etc/audit/audit.rules
    ansible.builtin.find:
      paths: /etc/audit
      contains: -a always,exit -F arch=b64(( -S |,)\w+)*(( -S |,){{ item }})+(( -S
        |,)\w+)* -F auid>=1000 -F auid!=unset (-k\s+|-F\s+key=)\S+\s*$
      patterns: audit.rules
    register: find_command
    loop: '{{ (syscall_grouping + syscalls) | unique }}'

  - name: Set path to /etc/audit/audit.rules
    ansible.builtin.set_fact: audit_file="/etc/audit/audit.rules"

  - name: Declare found syscalls
    ansible.builtin.set_fact: syscalls_found="{{ find_command.results | selectattr('matched')
      | map(attribute='item') | list }}"

  - name: Declare missing syscalls
    ansible.builtin.set_fact: missing_syscalls="{{ syscalls | difference(syscalls_found)
      }}"

  - name: Replace the audit rule in {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      regexp: (-a always,exit -F arch=b64)(?=.*(?:(?:-S |,)(?:{{ syscalls_found |
        join("|") }}))\b)((?:( -S |,)\w+)+)( -F auid>=1000 -F auid!=unset (?:-k |-F
        key=)\w+)
      line: \1\2\3{{ missing_syscalls | join("\3") }}\4
      backrefs: true
      state: present
      mode: g-rwx,o-rwx
    when: syscalls_found | length > 0 and missing_syscalls | length > 0

  - name: Add the audit rule to {{ audit_file }}
    ansible.builtin.lineinfile:
      path: '{{ audit_file }}'
      line: -a always,exit -F arch=b64 -S {{ syscalls | join(',') }} -F auid>=1000
        -F auid!=unset -F key=export
      create: true
      mode: g-rwx,o-rwx
      state: present
    when: syscalls_found | length == 0
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - audit_arch == "b64"
  tags:
  - CCE-80722-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030302
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.7
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.7
  - audit_rules_media_export
  - low_complexity
  - low_disruption
  - medium_severity
  - reboot_required
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit mount  oval:ssg-test_32bit_ardm_mount_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_mount_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit augenrules 64-bit mount  oval:ssg-test_64bit_ardm_mount_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_mount_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit mount  oval:ssg-test_32bit_ardm_mount_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_ardm_mount_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

64 bit architecture  oval:ssg-test_system_info_architecture_x86_64:tst:1  true

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
truex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppc_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_ppcle_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_aarch_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

64 bit architecture  oval:ssg-test_system_info_architecture_s390_64:tst:1  false

Following items have been found on the system:
Result of item-state comparisonMachine classNode nameOs nameOs releaseOs versionProcessor type
falsex86_64localhost.localdomainLinux4.18.0-553.el8_10.x86_64#1 SMP Fri May 10 15:19:13 EDT 2024x86_64

audit auditctl 64-bit mount  oval:ssg-test_64bit_ardm_mount_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_ardm_mount_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+mount[\s]+|([\s]+|[,])mount([\s]+|[,])))(?:.*-F\s+auid>=1000[\s]+)(?:.*-F\s+auid!=(?:4294967295|unset)[\s]+).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Ensure auditd Collects System Administrator Actions - /etc/sudoersxccdf_org.ssgproject.content_rule_audit_rules_sudoers mediumCCE-90175-1

Ensure auditd Collects System Administrator Actions - /etc/sudoers

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_sudoers
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_sudoers:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-90175-1

References:
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
stigidRHEL-08-030171
stigrefSV-230409r1017215_rule
Description
At a minimum, the audit system should collect administrator actions for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/sudoers -p wa -k actions
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/sudoers -p wa -k actions
Rationale
The actions taken by system administrators should be audited to keep a record of what was executed on the system, as well as, for accountability purposes. Editing the sudoers file may be sign of an attacker trying to establish persistent methods to a system, auditing the editing of the sudoers files mitigates this risk.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/actions.rules"
    # If the actions.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Check
    if watch rule for /etc/sudoers already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Search
    /etc/audit/rules.d for other rules with specified key actions
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)actions$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Use /etc/audit/rules.d/actions.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/actions.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Add watch
    rule for /etc/sudoers in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/sudoers -p wa -k actions
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Check
    if watch rule for /etc/sudoers already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/sudoers\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers - Add watch
    rule for /etc/sudoers in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/sudoers -p wa -k actions
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-90175-1
  - DISA-STIG-RHEL-08-030171
  - audit_rules_sudoers
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules sudoers  oval:ssg-test_audit_rules_sudoers_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_sudoers_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl sudoers  oval:ssg-test_audit_rules_sudoers_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_sudoers_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/sudoers[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/xccdf_org.ssgproject.content_rule_audit_rules_sudoers_d mediumCCE-89497-2

Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_sudoers_d
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_sudoers_d:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-89497-2

References:
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
stigidRHEL-08-030172
stigrefSV-230410r1017216_rule
Description
At a minimum, the audit system should collect administrator actions for all users and root. If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/sudoers.d/ -p wa -k actions
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/sudoers.d/ -p wa -k actions
Rationale
The actions taken by system administrators should be audited to keep a record of what was executed on the system, as well as, for accountability purposes. Editing the sudoers file may be sign of an attacker trying to establish persistent methods to a system, auditing the editing of the sudoers files mitigates this risk.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/actions.rules"
    # If the actions.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Check
    if watch rule for /etc/sudoers.d/ already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Search
    /etc/audit/rules.d for other rules with specified key actions
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)actions$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Use
    /etc/audit/rules.d/actions.rules as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/actions.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Use
    matched file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Add
    watch rule for /etc/sudoers.d/ in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/sudoers.d/ -p wa -k actions
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Check
    if watch rule for /etc/sudoers.d/ already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/sudoers.d/\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/ - Add
    watch rule for /etc/sudoers.d/ in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/sudoers.d/ -p wa -k actions
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-89497-2
  - DISA-STIG-RHEL-08-030172
  - audit_rules_sudoers_d
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules sudoers_d  oval:ssg-test_audit_rules_sudoers_d_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_sudoers_d_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/sudoers.d\/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl sudoers_d  oval:ssg-test_audit_rules_sudoers_d_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_sudoers_d_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/sudoers.d\/[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Record Events When Privileged Executables Are Runxccdf_org.ssgproject.content_rule_audit_rules_suid_privilege_function mediumCCE-83556-1

Record Events When Privileged Executables Are Run

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_suid_privilege_function
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_suid_privilege_function:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-83556-1

References:
nistCM-5(1), AU-7(a), AU-7(b), AU-8(b), AU-12(3), AC-6(9)
os-srgSRG-OS-000326-GPOS-00126, SRG-OS-000327-GPOS-00127, SRG-OS-000755-GPOS-00220
app-srg-ctrSRG-APP-000343-CTR-000780, SRG-APP-000381-CTR-000905
pcidss410.2.1.2, 10.2.1, 10.2
stigidRHEL-08-030000
stigrefSV-230386r958730_rule
Description
Verify the system generates an audit record when privileged functions are executed. If audit is using the "auditctl" tool to load the rules, run the following command:
$ sudo grep execve /etc/audit/audit.rules
If audit is using the "augenrules" tool to load the rules, run the following command:
$ sudo grep -r execve /etc/audit/rules.d
-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid
-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid
-a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid
-a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid
If both the "b32" and "b64" audit rules for "SUID" files are not defined, this is a finding. If both the "b32" and "b64" audit rules for "SGID" files are not defined, this is a finding.
Rationale
Misuse of privileged functions, either intentionally or unintentionally by authorized users, or by unauthorized external entities that have compromised information system accounts, is a serious and ongoing concern and can have significant adverse impacts on organizations. Auditing the use of privileged functions is one way to detect such misuse and identify the risk from insider threats and the advanced persistent threat.
Warnings
warning  Note that these rules can be configured in a number of ways while still achieving the desired effect.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
    
	OTHER_FILTERS="-C uid!=euid -F euid=0"
	
	AUID_FILTERS=""
	SYSCALL="execve"
    
	KEY="setuid"
	
	SYSCALL_GROUPING=""
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

for ARCH in "${RULE_ARCHS[@]}"
do
	ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH"
    
	OTHER_FILTERS="-C gid!=egid -F egid=0"
	
	AUID_FILTERS=""
	SYSCALL="execve"
    
	KEY="setgid"
	
	SYSCALL_GROUPING=""
	# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()

# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
default_file="/etc/audit/rules.d/$KEY.rules"
# As other_filters may include paths, lets use a different delimiter for it
# The "F" script expression tells sed to print the filenames where the expressions matched
readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules)
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
    file_to_inspect="/etc/audit/rules.d/$KEY.rules"
    files_to_inspect=("$file_to_inspect")
    if [ ! -e "$file_to_inspect" ]
    then
        touch "$file_to_inspect"
        chmod 0600 "$file_to_inspect"
    fi
fi

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
	unset syscall_a
unset syscall_grouping
unset syscall_string
unset syscall
unset file_to_edit
unset rule_to_edit
unset rule_syscalls_to_edit
unset other_string
unset auid_string
unset full_rule

# Load macro arguments into arrays
read -a syscall_a <<< $SYSCALL
read -a syscall_grouping <<< $SYSCALL_GROUPING

# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
#  Tool used to load audit rules | Rule already defined  |  Audit rules file to inspect    |
# -----------------------------------------------------------------------------------------
#        auditctl                |     Doesn't matter    |  /etc/audit/audit.rules         |
# -----------------------------------------------------------------------------------------
#        augenrules              |          Yes          |  /etc/audit/rules.d/*.rules     |
#        augenrules              |          No           |  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
#
files_to_inspect=()


# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
default_file="/etc/audit/audit.rules"
files_to_inspect+=('/etc/audit/audit.rules' )

# After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead
skip=1

for audit_file in "${files_to_inspect[@]}"
do
    # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern,
    # i.e, collect rules that match:
    # * the action, list and arch, (2-nd argument)
    # * the other filters, (3-rd argument)
    # * the auid filters, (4-rd argument)
    readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d"  -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file")

    candidate_rules=()
    # Filter out rules that have more fields then required. This will remove rules more specific than the required scope
    for s_rule in "${similar_rules[@]}"
    do
        # Strip all the options and fields we know of,
        # than check if there was any field left over
        extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//"  -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule")
        grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule")
    done

    if [[ ${#syscall_a[@]} -ge 1 ]]
    then
        # Check if the syscall we want is present in any of the similar existing rules
        for rule in "${candidate_rules[@]}"
        do
            rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs)
            all_syscalls_found=0
            for syscall in "${syscall_a[@]}"
            do
                grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || {
                   # A syscall was not found in the candidate rule
                   all_syscalls_found=1
                   }
            done
            if [[ $all_syscalls_found -eq 0 ]]
            then
                # We found a rule with all the syscall(s) we want; skip rest of macro
                skip=0
                break
            fi

            # Check if this rule can be grouped with our target syscall and keep track of it
            for syscall_g in "${syscall_grouping[@]}"
            do
                if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls"
                then
                    file_to_edit=${audit_file}
                    rule_to_edit=${rule}
                    rule_syscalls_to_edit=${rule_syscalls}
                fi
            done
        done
    else
        # If there is any candidate rule, it is compliant; skip rest of macro
        if [ "${#candidate_rules[@]}" -gt 0 ]
        then
            skip=0
        fi
    fi

    if [ "$skip" -eq 0 ]; then
        break
    fi
done

if [ "$skip" -ne 0 ]; then
    # We checked all rules that matched the expected resemblance pattern (action, arch & auid)
    # At this point we know if we need to either append the $full_rule or group
    # the syscall together with an exsiting rule

    # Append the full_rule if it cannot be grouped to any other rule
    if [ -z ${rule_to_edit+x} ]
    then
        # Build full_rule while avoid adding double spaces when other_filters is empty
        if [ "${#syscall_a[@]}" -gt 0 ]
        then
            syscall_string=""
            for syscall in "${syscall_a[@]}"
            do
                syscall_string+=" -S $syscall"
            done
        fi
        other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true
        auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true
        full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true
        echo "$full_rule" >> "$default_file"
        chmod 0600 ${default_file}
    else
        # Check if the syscalls are declared as a comma separated list or
        # as multiple -S parameters
        if grep -q -- "," <<< "${rule_syscalls_to_edit}"
        then
            delimiter=","
        else
            delimiter=" -S "
        fi
        new_grouped_syscalls="${rule_syscalls_to_edit}"
        for syscall in "${syscall_a[@]}"
        do
            grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || {
               # A syscall was not found in the candidate rule
               new_grouped_syscalls+="${delimiter}${syscall}"
               }
        done

        # Group the syscall in the rule
        sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit"
    fi
fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Service facts
  ansible.builtin.service_facts: null
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set suid_audit_rules fact
  ansible.builtin.set_fact:
    suid_audit_rules:
    - rule: -a always,exit -F arch=b32 -S execve -C gid!=egid -F egid=0 -k setgid
      regex: ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
    - rule: -a always,exit -F arch=b64 -S execve -C gid!=egid -F egid=0 -k setgid
      regex: ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
    - rule: -a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid
      regex: ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
    - rule: -a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid
      regex: ^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Update /etc/audit/rules.d/privileged.rules to audit privileged functions
  ansible.builtin.lineinfile:
    path: /etc/audit/rules.d/privileged.rules
    line: '{{  item.rule  }}'
    regexp: '{{ item.regex }}'
    mode: '0600'
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ('"auditd.service" in ansible_facts.services' or '"augenrules.service" in ansible_facts.services')
  register: augenrules_audit_rules_privilege_function_update_result
  with_items: '{{ suid_audit_rules }}'
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Update /etc/audit/audit.rules to audit privileged functions
  ansible.builtin.lineinfile:
    path: /etc/audit/audit.rules
    line: '{{  item.rule  }}'
    regexp: '{{ item.regex }}'
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - ('"auditd.service" in ansible_facts.services' or '"augenrules.service" in ansible_facts.services')
  register: auditctl_audit_rules_privilege_function_update_result
  with_items: '{{ suid_audit_rules }}'
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Restart Auditd
  ansible.builtin.command: /usr/sbin/service auditd restart
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - (augenrules_audit_rules_privilege_function_update_result.changed or auditctl_audit_rules_privilege_function_update_result.changed)
  - ansible_facts.services["auditd.service"].state == "running"
  tags:
  - CCE-83556-1
  - DISA-STIG-RHEL-08-030000
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(3)
  - NIST-800-53-AU-7(a)
  - NIST-800-53-AU-7(b)
  - NIST-800-53-AU-8(b)
  - NIST-800-53-CM-5(1)
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.2
  - audit_rules_suid_privilege_function
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ -a%20always%2Cexit%20-F%20arch%3Db32%20-S%20execve%20-C%20uid%21%3Deuid%20-F%20euid%3D0%20-k%20execpriv%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20execve%20-C%20uid%21%3Deuid%20-F%20euid%3D0%20-k%20execpriv%0A-a%20always%2Cexit%20-F%20arch%3Db32%20-S%20execve%20-C%20gid%21%3Degid%20-F%20egid%3D0%20-k%20execpriv%0A-a%20always%2Cexit%20-F%20arch%3Db64%20-S%20execve%20-C%20gid%21%3Degid%20-F%20egid%3D0%20-k%20execpriv%0A }}
        mode: 0600
        path: /etc/audit/rules.d/75-audit-suid-privilege-function.rules
        overwrite: true
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules 32-bit uid privileged function  oval:ssg-test_32bit_uid_privileged_function_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_uid_privileged_function_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit uid privileged function  oval:ssg-test_64bit_uid_privileged_function_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_uid_privileged_function_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 32-bit gid privileged function  oval:ssg-test_32bit_gid_privileged_function_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_gid_privileged_function_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit augenrules 64-bit gid privileged function  oval:ssg-test_64bit_gid_privileged_function_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_gid_privileged_function_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^/etc/audit/rules\.d/.*\.rules$^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl 32-bit uid privileged function  oval:ssg-test_32bit_uid_privileged_function_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_uid_privileged_function_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit uid privileged_function  oval:ssg-test_64bit_uid_privileged_function_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_uid_privileged_function_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+uid!=euid[\s]+-F[\s]+euid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 32-bit gid privileged function  oval:ssg-test_32bit_gid_privileged_function_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_32bit_gid_privileged_function_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b32[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1

audit auditctl 64-bit gid privileged_function  oval:ssg-test_64bit_gid_privileged_function_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_64bit_gid_privileged_function_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/audit.rules^[\s]*-a[\s]+always,exit[\s]+-F[\s]+arch=b64[\s]+-S[\s]+execve[\s]+-C[\s]+gid!=egid[\s]+-F[\s]+egid=0[\s]+(?:-k[\s]+|-F[\s]+key=)[\S]+[\s]*$1
Record Events that Modify User/Group Information - /etc/groupxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_group mediumCCE-80758-6

Record Events that Modify User/Group Information - /etc/group

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_group
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_usergroup_modification_group:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80758-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.5
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
anssiR73
cis5.2.3.8
pcidss410.2.1.5, 10.2.1, 10.2
stigidRHEL-08-030170
stigrefSV-230408r1017214_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/group -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/group -p wa -k audit_rules_usergroup_modification
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules"
    # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Check if watch
    rule for /etc/group already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Search /etc/audit/rules.d
    for other rules with specified key audit_rules_usergroup_modification
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/audit_rules_usergroup_modification.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Add watch
    rule for /etc/group in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/group -p wa -k audit_rules_usergroup_modification
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Check if watch
    rule for /etc/group already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/group\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/group - Add watch
    rule for /etc/group in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/group -p wa -k audit_rules_usergroup_modification
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-80758-6
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030170
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_group
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules group  oval:ssg-test_audit_rules_usergroup_modification_group_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_group_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl group  oval:ssg-test_audit_rules_usergroup_modification_group_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_group_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/group[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Record Events that Modify User/Group Information - /etc/gshadowxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_gshadow mediumCCE-80759-4

Record Events that Modify User/Group Information - /etc/gshadow

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_gshadow
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_usergroup_modification_gshadow:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80759-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.5
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
anssiR73
cis5.2.3.8
pcidss410.2.1.5, 10.2.1, 10.2
stigidRHEL-08-030160
stigrefSV-230407r1017213_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/gshadow -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/gshadow -p wa -k audit_rules_usergroup_modification
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules"
    # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Check if
    watch rule for /etc/gshadow already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Search /etc/audit/rules.d
    for other rules with specified key audit_rules_usergroup_modification
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/audit_rules_usergroup_modification.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Add watch
    rule for /etc/gshadow in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Check if
    watch rule for /etc/gshadow already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/gshadow\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/gshadow - Add watch
    rule for /etc/gshadow in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/gshadow -p wa -k audit_rules_usergroup_modification
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-80759-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030160
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_gshadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules gshadow  oval:ssg-test_audit_rules_usergroup_modification_gshadow_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_gshadow_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl gshadow  oval:ssg-test_audit_rules_usergroup_modification_gshadow_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_gshadow_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/gshadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Record Events that Modify User/Group Information - /etc/security/opasswdxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_opasswd mediumCCE-80760-2

Record Events that Modify User/Group Information - /etc/security/opasswd

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_opasswd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_usergroup_modification_opasswd:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80760-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.5
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000496-CTR-001240, SRG-APP-000497-CTR-001245, SRG-APP-000498-CTR-001250, SRG-APP-000503-CTR-001275
anssiR73
cis5.2.3.8
pcidss410.2.1.5, 10.2.1, 10.2
stigidRHEL-08-030140
stigrefSV-230405r1017211_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules"
    # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Check if watch rule for /etc/security/opasswd already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Search /etc/audit/rules.d for other rules with specified key audit_rules_usergroup_modification
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules as the recipient
    for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/audit_rules_usergroup_modification.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Use matched file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Add watch rule for /etc/security/opasswd in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Check if watch rule for /etc/security/opasswd already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/security/opasswd\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/security/opasswd -
    Add watch rule for /etc/security/opasswd in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-80760-2
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030140
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_opasswd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules opasswd  oval:ssg-test_audit_rules_usergroup_modification_opasswd_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_opasswd_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl opasswd  oval:ssg-test_audit_rules_usergroup_modification_opasswd_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_opasswd_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/security\/opasswd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Record Events that Modify User/Group Information - /etc/passwdxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_passwd mediumCCE-80761-0

Record Events that Modify User/Group Information - /etc/passwd

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_passwd
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_usergroup_modification_passwd:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80761-0

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.5
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000304-GPOS-00121, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221, SRG-OS-000274-GPOS-00104, SRG-OS-000275-GPOS-00105, SRG-OS-000276-GPOS-00106, SRG-OS-000277-GPOS-00107
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
anssiR73
cis5.2.3.8
pcidss410.2.1.5, 10.2.1, 10.2
stigidRHEL-08-030150
stigrefSV-230406r1017212_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/passwd -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/passwd -p wa -k audit_rules_usergroup_modification
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules"
    # If the audit_rules_usergroup_modification_passwd.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Check if
    watch rule for /etc/passwd already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Search /etc/audit/rules.d
    for other rules with specified key audit_rules_usergroup_modification_passwd
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification_passwd$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Use /etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Add watch
    rule for /etc/passwd in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Check if
    watch rule for /etc/passwd already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/passwd\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/passwd - Add watch
    rule for /etc/passwd in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-80761-0
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030150
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_passwd
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules passwd  oval:ssg-test_audit_rules_usergroup_modification_passwd_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_passwd_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl passwd  oval:ssg-test_audit_rules_usergroup_modification_passwd_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_passwd_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/passwd[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Record Events that Modify User/Group Information - /etc/shadowxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_shadow mediumCCE-80762-8

Record Events that Modify User/Group Information - /etc/shadow

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_shadow
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_usergroup_modification_shadow:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80762-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.1.7
hipaa164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5
nerc-cipCIP-004-6 R2.2.2, CIP-004-6 R2.2.3, CIP-007-3 R.1.3, CIP-007-3 R5, CIP-007-3 R5.1.1, CIP-007-3 R5.1.3, CIP-007-3 R5.2.1, CIP-007-3 R5.2.3
nistAC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
pcidssReq-10.2.5
os-srgSRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000304-GPOS-00121, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000470-GPOS-00214, SRG-OS-000471-GPOS-00215, SRG-OS-000239-GPOS-00089, SRG-OS-000240-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000466-GPOS-00210, SRG-OS-000476-GPOS-00221
app-srg-ctrSRG-APP-000495-CTR-001235, SRG-APP-000499-CTR-001255, SRG-APP-000503-CTR-001275
anssiR73
cis5.2.3.8
pcidss410.2.1.5, 10.2.1, 10.2
stigidRHEL-08-030130
stigrefSV-230404r1017210_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /etc/shadow -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /etc/shadow -p wa -k audit_rules_usergroup_modification
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules"
    # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Check if
    watch rule for /etc/shadow already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Search /etc/audit/rules.d
    for other rules with specified key audit_rules_usergroup_modification
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)audit_rules_usergroup_modification$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Use /etc/audit/rules.d/audit_rules_usergroup_modification.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/audit_rules_usergroup_modification.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Add watch
    rule for /etc/shadow in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Check if
    watch rule for /etc/shadow already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/etc/shadow\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Record Events that Modify User/Group Information - /etc/shadow - Add watch
    rule for /etc/shadow in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /etc/shadow -p wa -k audit_rules_usergroup_modification
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-80762-8
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030130
  - NIST-800-171-3.1.7
  - NIST-800-53-AC-2(4)
  - NIST-800-53-AC-6(9)
  - NIST-800-53-AU-12(c)
  - NIST-800-53-AU-2(d)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.2.5
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.1
  - PCI-DSSv4-10.2.1.5
  - audit_rules_usergroup_modification_shadow
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules shadow  oval:ssg-test_audit_rules_usergroup_modification_shadow_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_shadow_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl shadow  oval:ssg-test_audit_rules_usergroup_modification_shadow_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_usergroup_modification_shadow_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/etc\/shadow[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
Ensure auditd Collects Changes to Cron Jobs - /var/spool/cronxccdf_org.ssgproject.content_rule_audit_rules_var_spool_cron mediumCCE-86635-0

Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron

Rule IDxccdf_org.ssgproject.content_rule_audit_rules_var_spool_cron
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-audit_rules_var_spool_cron:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-86635-0

References:
os-srgSRG-OS-000363-GPOS-00150, SRG-OS-000363-GPOS-00150, SRG-OS-000446-GPOS-00200, SRG-OS-000447-GPOS-00201
stigidRHEL-08-030655
stigrefSV-274877r1106148_rule
Description
If the auditd daemon is configured to use the augenrules program to read audit rules during daemon startup (the default), add the following lines to a file with suffix .rules in the directory /etc/audit/rules.d:
-w /var/spool/cron -p wa -k cronjobs
If the auditd daemon is configured to use the auditctl utility to read audit rules during daemon startup, add the following lines to /etc/audit/audit.rules:
-w /var/spool/cron -p wa -k cronjobs
Rationale
In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'






# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()


# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/var/spool/cron" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/spool/cron $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/var/spool/cron$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /var/spool/cron -p wa -k cronjobs" >> "$audit_rules_file"

    fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules	| Rule already defined	|  Audit rules file to inspect	  |
# -----------------------------------------------------------------------------------------
#	auditctl		|     Doesn't matter	|  /etc/audit/audit.rules	  |
# -----------------------------------------------------------------------------------------
# 	augenrules		|          Yes		|  /etc/audit/rules.d/*.rules	  |
# 	augenrules		|          No		|  /etc/audit/rules.d/$key.rules  |
# -----------------------------------------------------------------------------------------
files_to_inspect=()

# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/cronjobs.rules' to list of files for inspection.

readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/spool/cron" /etc/audit/rules.d/*.rules)


# For each of the matched entries
for match in "${matches[@]}"
do
    # Extract filepath from the match
    rulesd_audit_file=$(echo $match | cut -f1 -d ':')
    # Append that path into list of files for inspection
    files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
    # Append '/etc/audit/rules.d/cronjobs.rules' into list of files for inspection
    key_rule_file="/etc/audit/rules.d/cronjobs.rules"
    # If the cronjobs.rules file doesn't exist yet, create it with correct permissions
    if [ ! -e "$key_rule_file" ]
    then
        touch "$key_rule_file"
        chmod 0600 "$key_rule_file"
    fi
    files_to_inspect+=("$key_rule_file")
fi

# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
    # Check if audit watch file system object rule for given path already present

    if grep -q -P -- "^[\s]*-w[\s]+/var/spool/cron" "$audit_rules_file"

    then
        # Rule is found => verify yet if existing rule definition contains
        # all of the required access type bits

        # Define BRE whitespace class shortcut
        sp="[[:space:]]"
        # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule

        current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/spool/cron $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")

        # Split required access bits string into characters array
        # (to check bit's presence for one bit at a time)
        for access_bit in $(echo "wa" | grep -o .)
        do
            # For each from the required access bits (e.g. 'w', 'a') check
            # if they are already present in current access bits for rule.
            # If not, append that bit at the end
            if ! grep -q "$access_bit" <<< "$current_access_bits"
            then
                # Concatenate the existing mask with the missing bit
                current_access_bits="$current_access_bits$access_bit"
            fi
        done
        # Propagate the updated rule's access bits (original + the required
        # ones) back into the /etc/audit/audit.rules file for that rule

        sed -i "s#\($sp*-w$sp\+/var/spool/cron$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"

    else
        # Rule isn't present yet. Append it at the end of $audit_rules_file file
        # with proper key


        echo "-w /var/spool/cron -p wa -k cronjobs" >> "$audit_rules_file"

    fi
done

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Check if watch
    rule for /var/spool/cron already exists in /etc/audit/rules.d/
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^\s*-w\s+/var/spool/cron\s+-p\s+wa(\s|$)+
    patterns: '*.rules'
  register: find_existing_watch_rules_d
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Search /etc/audit/rules.d
    for other rules with specified key cronjobs
  ansible.builtin.find:
    paths: /etc/audit/rules.d
    contains: ^.*(?:-F key=|-k\s+)cronjobs$
    patterns: '*.rules'
  register: find_watch_key
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Use /etc/audit/rules.d/cronjobs.rules
    as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - /etc/audit/rules.d/cronjobs.rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Use matched
    file as the recipient for the rule
  ansible.builtin.set_fact:
    all_files:
    - '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
    is defined and find_existing_watch_rules_d.matched == 0
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Add watch
    rule for /var/spool/cron in /etc/audit/rules.d/
  ansible.builtin.lineinfile:
    path: '{{ all_files[0] }}'
    line: -w /var/spool/cron -p wa -k cronjobs
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
    == 0
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Check if watch
    rule for /var/spool/cron already exists in /etc/audit/audit.rules
  ansible.builtin.find:
    paths: /etc/audit/
    contains: ^\s*-w\s+/var/spool/cron\s+-p\s+wa(\s|$)+
    patterns: audit.rules
  register: find_existing_watch_audit_rules
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Ensure auditd Collects Changes to Cron Jobs - /var/spool/cron - Add watch
    rule for /var/spool/cron in /etc/audit/audit.rules
  ansible.builtin.lineinfile:
    line: -w /var/spool/cron -p wa -k cronjobs
    state: present
    dest: /etc/audit/audit.rules
    create: true
    mode: '0600'
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  - find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
    == 0
  tags:
  - CCE-86635-0
  - DISA-STIG-RHEL-08-030655
  - audit_rules_var_spool_cron
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

audit augenrules  oval:ssg-test_audit_rules_augenrules:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/usr/lib/systemd/system/auditd.serviceExecStartPost=-/sbin/augenrules --load

audit augenrules cron  oval:ssg-test_audit_rules_var_spool_cron_augenrules:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_var_spool_cron_augenrules:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/var\/spool\/cron[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$^/etc/audit/rules\.d/.*\.rules$1

audit auditctl  oval:ssg-test_audit_rules_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/usr/lib/systemd/system/auditd.service^ExecStartPost=\-\/sbin\/auditctl.*$1

audit auditctl cron  oval:ssg-test_audit_rules_var_spool_cron_auditctl:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_rules_var_spool_cron_auditctl:obj:1 of type textfilecontent54_object
FilepathPatternInstance
^\-w[\s]+\/var\/spool\/cron[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$/etc/audit/audit.rules1
System Audit Directories Must Be Group Owned By Rootxccdf_org.ssgproject.content_rule_directory_group_ownership_var_log_audit mediumCCE-88225-8

System Audit Directories Must Be Group Owned By Root

Rule IDxccdf_org.ssgproject.content_rule_directory_group_ownership_var_log_audit
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-directory_group_ownership_var_log_audit:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-88225-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
cui3.3.1
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1), AU-9(4)
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5.1
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084
stigidRHEL-08-030110
stigrefSV-230400r1017206_rule
Description
All audit directories must be group owned by root user. By default, the path for audit log is
/var/log/audit/
. To properly set the group owner of /var/log/audit, run the command:
$ sudo chgrp root /var/log/audit
If log_group in /etc/audit/auditd.conf is set to a group other than the root group account, change the group ownership of the audit directories to this specific group.
Rationale
Unauthorized disclosure of audit records can reveal system and configuration data to attackers, thus compromising its confidentiality.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

/var/log/audit directories uid root gid root  oval:ssg-test_group_ownership_var_log_audit_directories:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_group_ownership_var_log_audit_directories:obj:1 of type file_object
BehaviorsPathFilenameFilter
/var/log/audit/audit.log
/var/log/audit
no valueno valueoval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

/var/log/audit directories uid root gid root  oval:ssg-test_group_ownership_default_var_log_audit_directories:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_group_ownership_default_var_log_audit_directories:obj:1 of type file_object
BehaviorsPathFilenameFilter
no value/var/log/auditno valueoval:ssg-state_group_owner_not_root_var_log_audit_directories:ste:1

log_group = root  oval:ssg-test_auditd_conf_log_group_not_root:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root

log_group is set  oval:ssg-test_auditd_conf_log_group_is_set:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root

/var/log/audit directories uid root gid root  oval:ssg-test_group_ownership_var_log_audit_directories-non_root:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_group_ownership_var_log_audit_directories-non_root:obj:1 of type file_object
BehaviorsPathFilenameFilter
no value/var/log/auditno valueoval:ssg-state_group_owner_not_root_var_log_audit_directories-non_root:ste:1
System Audit Directories Must Be Owned By Rootxccdf_org.ssgproject.content_rule_directory_ownership_var_log_audit mediumCCE-88226-6

System Audit Directories Must Be Owned By Root

Rule IDxccdf_org.ssgproject.content_rule_directory_ownership_var_log_audit
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-directory_ownership_var_log_audit:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-88226-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
cui3.3.1
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1), AU-9(4)
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5.1
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084
stigidRHEL-08-030100
stigrefSV-230399r1017205_rule
Description
All audit directories must be owned by root user. By default, the path for audit log is
/var/log/audit/
. To properly set the owner of /var/log/audit, run the command:
$ sudo chown root /var/log/audit 
Rationale
Unauthorized disclosure of audit records can reveal system and configuration data to attackers, thus compromising its confidentiality.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

log_file's directory uid root gid root  oval:ssg-test_user_ownership_var_log_audit_path:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_user_ownership_var_log_audit_path:obj:1 of type file_object
PathFilenameFilter
/var/log/audit
/var/log/audit/audit.log
no valueoval:ssg-state_owner_not_root_var_log_audit_directories:ste:1

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

/var/log/audit directories uid root gid root  oval:ssg-test_user_ownership_var_log_audit_directories:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_user_ownership_var_log_audit_directories:obj:1 of type file_object
PathFilenameFilter
/var/log/auditno valueoval:ssg-state_owner_not_root_var_log_audit_directories:ste:1
System Audit Logs Must Have Mode 0750 or Less Permissivexccdf_org.ssgproject.content_rule_directory_permissions_var_log_audit mediumCCE-84048-8

System Audit Logs Must Have Mode 0750 or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_directory_permissions_var_log_audit
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-directory_permissions_var_log_audit:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-84048-8

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.2, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-004-6 R3.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2, CIP-007-3 R6.5
nistCM-6(a), AC-6(1), AU-9
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029
cis5.2.4.1
stigidRHEL-08-030120
stigrefSV-230401r1017207_rule
Description
Verify the audit log directories have a mode of "0700" or less permissive by first determining where the audit logs are stored with the following command:
$ sudo grep -iw log_file /etc/audit/auditd.conf

log_file = /var/log/audit/audit.log
Configure the audit log directory to be protected from unauthorized read access by setting the correct permissive mode with the following command:
$ sudo chmod 0700 audit_log_directory
By default, audit_log_directory is "/var/log/audit".
Rationale
If users can write to audit logs, audit trails can be modified or destroyed.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

/var/log/audit mode 0700  oval:ssg-test_dir_permissions_audit_log:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_log_directory:obj:1 of type file_object
PathFilenameFilter
/var/log/audit/audit.log
/var/log/audit
no valueoval:ssg-state_not_mode_0700:ste:1

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

/var/log/audit mode 0700  oval:ssg-test_dir_permissions_var_log_audit:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_var_log_audit_directory:obj:1 of type file_object
PathFilenameFilter
/var/log/auditno valueoval:ssg-state_not_mode_0700:ste:1
System Audit Logs Must Be Group Owned By Rootxccdf_org.ssgproject.content_rule_file_group_ownership_var_log_audit mediumCCE-88227-4

System Audit Logs Must Be Group Owned By Root

Rule IDxccdf_org.ssgproject.content_rule_file_group_ownership_var_log_audit
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_group_ownership_var_log_audit:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-88227-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
cui3.3.1
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1), AU-9(4)
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5.1
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084
cis5.2.4.4
pcidss410.3.2, 10.3
stigidRHEL-08-030090
stigrefSV-230398r1017204_rule
Description
All audit logs must be group owned by root user. The path for audit log can be configured via log_file parameter in
/etc/audit/auditd.conf
or, by default, the path for audit log is
/var/log/audit/
. To properly set the group owner of /var/log/audit/*, run the command:
$ sudo chgrp root /var/log/audit/*
If log_group in /etc/audit/auditd.conf is set to a group other than the root group account, change the group ownership of the audit logs to this specific group.
Rationale
Unauthorized disclosure of audit records can reveal system and configuration data to attackers, thus compromising its confidentiality.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

audit log files gid root  oval:ssg-test_group_ownership_audit_log_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_group_ownership_audit_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_group_owner_not_root_var_log_audit:ste:1

log_group = root  oval:ssg-test_auditd_conf_log_group_not_root:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root

log_group is set  oval:ssg-test_auditd_conf_log_group_is_set:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

audit log files gid root  oval:ssg-test_group_ownership_default_audit_log_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_group_ownership_default_audit_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_group_owner_not_root_var_log_audit:ste:1

log_group = root  oval:ssg-test_auditd_conf_log_group_not_root:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root

log_group is set  oval:ssg-test_auditd_conf_log_group_is_set:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_group = root
System Audit Logs Must Be Owned By Rootxccdf_org.ssgproject.content_rule_file_ownership_var_log_audit_stig mediumCCE-88228-2

System Audit Logs Must Be Owned By Root

Rule IDxccdf_org.ssgproject.content_rule_file_ownership_var_log_audit_stig
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_ownership_var_log_audit_stig:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-88228-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
cui3.3.1
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nistCM-6(a), AC-6(1), AU-9(4)
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5.1
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084
cis5.2.4.3
stigidRHEL-08-030080
stigrefSV-230397r1017203_rule
Description
All audit logs must be owned by root user. The path for audit log can be configured via log_file parameter in
/etc/audit/auditd.conf
or by default, the path for audit log is
/var/log/audit/
. To properly set the owner of /var/log/audit/*, run the command:
$ sudo chown root /var/log/audit/* 
Rationale
Unauthorized disclosure of audit records can reveal system and configuration data to attackers, thus compromising its confidentiality.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

audit log files uid root  oval:ssg-test_user_ownership_audit_log_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_user_ownership_audit_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_owner_not_root_var_log_audit:ste:1

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

var/log/audit/audit.log file uid root  oval:ssg-test_user_ownership_audit_default_log_files:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_user_ownership_audit_default_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_owner_not_root_var_log_audit:ste:1
System Audit Logs Must Have Mode 0640 or Less Permissivexccdf_org.ssgproject.content_rule_file_permissions_var_log_audit mediumCCE-80819-6

System Audit Logs Must Have Mode 0640 or Less Permissive

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_var_log_audit
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_var_log_audit:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80819-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01
cui3.3.1
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1
iso27001-2013A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5
nerc-cipCIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.3, CIP-007-3 R2.1, CIP-007-3 R2.2, CIP-007-3 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.1, CIP-007-3 R5.1.2
nistCM-6(a), AC-6(1), AU-9(4)
nist-csfDE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.5
os-srgSRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084
app-srg-ctrSRG-APP-000118-CTR-000240
cis5.2.4.2
pcidss410.3.1, 10.3
stigidRHEL-08-030070
stigrefSV-230396r1017202_rule
Description
Determine where the audit logs are stored with the following command:
$ sudo grep -iw log_file /etc/audit/auditd.conf
log_file = /var/log/audit/audit.log
Configure the audit log to be protected from unauthorized read access by setting the correct permissive mode with the following command:
$ sudo chmod 0600 audit_log_file
By default, audit_log_file is "/var/log/audit/audit.log".
Rationale
If users can write to audit logs, audit trails can be modified or destroyed.
OVAL test results details

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

audit log files mode 0600  oval:ssg-test_file_permissions_audit_log:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_not_mode_0600:ste:1

log_file not set  oval:ssg-test_auditd_conf_log_file_not_set:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
not evaluated/etc/audit/auditd.conflog_file = /var/log/audit/audit.log

default audit log files mode 0600  oval:ssg-test_file_permissions_default_audit_log:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_audit_default_log_files:obj:1 of type file_object
FilepathFilter
/var/log/audit/audit.logoval:ssg-state_not_mode_0600:ste:1
Configure a Sufficiently Large Partition for Audit Logsxccdf_org.ssgproject.content_rule_auditd_audispd_configure_sufficiently_large_partition mediumCCE-84005-8

Configure a Sufficiently Large Partition for Audit Logs

Rule IDxccdf_org.ssgproject.content_rule_auditd_audispd_configure_sufficiently_large_partition
Result
notchecked
Multi-check ruleno
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-84005-8

References:
os-srgSRG-OS-000341-GPOS-00132, SRG-OS-000342-GPOS-00133
stigidRHEL-08-030660
stigrefSV-230476r958752_rule
Description
The Red Hat Enterprise Linux 8 operating system must allocate audit record storage capacity to store at least one weeks worth of audit records when audit records are not immediately sent to a central audit record storage facility. The partition size needed to capture a week's worth of audit records is based on the activity level of the system and the total storage capacity available. In normal circumstances, 10.0 GB of storage space for audit records will be sufficient. Determine which partition the audit records are being written to with the following command:
$ sudo grep log_file /etc/audit/auditd.conf
log_file = /var/log/audit/audit.log
Check the size of the partition that audit records are written to with the following command:
$ sudo df -h /var/log/audit/
/dev/sda2 24G 10.4G 13.6G 43% /var/log/audit
Rationale
Information stored in one location is vulnerable to accidental or incidental deletion or alteration. Off-loading is a common process in information systems with limited audit storage capacity.
Evaluation messages
info 
No candidate or applicable check found.
Configure auditd Disk Error Action on Disk Errorxccdf_org.ssgproject.content_rule_auditd_data_disk_error_action mediumCCE-84046-2

Configure auditd Disk Error Action on Disk Error

Rule IDxccdf_org.ssgproject.content_rule_auditd_data_disk_error_action
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_data_disk_error_action:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-84046-2

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8
cobit5APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1
nistAU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4
os-srgSRG-OS-000047-GPOS-00023
app-srg-ctrSRG-APP-000098-CTR-000185, SRG-APP-000099-CTR-000190, SRG-APP-000100-CTR-000195, SRG-APP-000100-CTR-000200, SRG-APP-000109-CTR-000215, SRG-APP-000290-CTR-000670, SRG-APP-000357-CTR-000800
cis5.2.2.3
stigidRHEL-08-030040
stigrefSV-230390r1038966_rule
Description
The auditd service can be configured to take an action when there is a disk error. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting ACTION appropriately:
disk_error_action = ACTION
Set this value to single to cause the system to switch to single-user mode for corrective action. Acceptable values also include syslog, exec, single, and halt For certain systems, the need for availability outweighs the need to log all actions, and a different setting should be determined. Details regarding all possible values for ACTION are described in the auditd.conf man page.
Rationale
Taking appropriate action in case of disk errors will minimize the possibility of losing audit records.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

var_auditd_disk_error_action='syslog|single|halt'


#
# If disk_error_action present in /etc/audit/auditd.conf, change value
# to var_auditd_disk_error_action, else
# add "disk_error_action = $var_auditd_disk_error_action" to /etc/audit/auditd.conf
#
var_auditd_disk_error_action="$(echo $var_auditd_disk_error_action | cut -d \| -f 1)"

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_error_action")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_error_action"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^disk_error_action\\>" "/etc/audit/auditd.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^disk_error_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf"
else
    if [[ -s "/etc/audit/auditd.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/audit/auditd.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/audit/auditd.conf"
    fi
    cce="CCE-84046-2"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf"
    printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84046-2
  - DISA-STIG-RHEL-08-030040
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - auditd_data_disk_error_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_auditd_disk_error_action # promote to variable
  set_fact:
    var_auditd_disk_error_action: !!str syslog|single|halt
  tags:
    - always

- name: Configure auditd Disk Error Action on Disk Error
  ansible.builtin.lineinfile:
    dest: /etc/audit/auditd.conf
    line: disk_error_action = {{ var_auditd_disk_error_action.split('|')[0] }}
    regexp: ^\s*disk_error_action\s*=\s*.*$
    state: present
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-84046-2
  - DISA-STIG-RHEL-08-030040
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - auditd_data_disk_error_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }}
        mode: 0640
        path: /etc/audit/auditd.conf
        overwrite: true
OVAL test results details

disk full action  oval:ssg-test_auditd_data_disk_error_action:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/audit/auditd.confdisk_error_action = SUSPEND
Configure auditd Disk Full Action when Disk Space Is Fullxccdf_org.ssgproject.content_rule_auditd_data_disk_full_action mediumCCE-84045-4

Configure auditd Disk Full Action when Disk Space Is Full

Rule IDxccdf_org.ssgproject.content_rule_auditd_data_disk_full_action
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_data_disk_full_action:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-84045-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8
cobit5APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1
nistAU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4
os-srgSRG-OS-000047-GPOS-00023
cis5.2.2.3
stigidRHEL-08-030060
stigrefSV-230392r1038966_rule
Description
The auditd service can be configured to take an action when disk space is running low but prior to running out of space completely. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting ACTION appropriately:
disk_full_action = ACTION
Set this value to single to cause the system to switch to single-user mode for corrective action. Acceptable values also include syslog, exec, single, and halt For certain systems, the need for availability outweighs the need to log all actions, and a different setting should be determined. Details regarding all possible values for ACTION are described in the auditd.conf man page.
Rationale
Taking appropriate action in case of a filled audit storage volume will minimize the possibility of losing audit records.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

var_auditd_disk_full_action='syslog|single|halt'


var_auditd_disk_full_action="$(echo $var_auditd_disk_full_action | cut -d \| -f 1)"

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^disk_full_action")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_disk_full_action"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^disk_full_action\\>" "/etc/audit/auditd.conf"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^disk_full_action\\>.*/$escaped_formatted_output/gi" "/etc/audit/auditd.conf"
else
    if [[ -s "/etc/audit/auditd.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/audit/auditd.conf" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/audit/auditd.conf"
    fi
    cce="CCE-84045-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/audit/auditd.conf" >> "/etc/audit/auditd.conf"
    printf '%s\n' "$formatted_output" >> "/etc/audit/auditd.conf"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-84045-4
  - DISA-STIG-RHEL-08-030060
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - auditd_data_disk_full_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_auditd_disk_full_action # promote to variable
  set_fact:
    var_auditd_disk_full_action: !!str syslog|single|halt
  tags:
    - always

- name: Configure auditd Disk Full Action when Disk Space Is Full
  ansible.builtin.lineinfile:
    dest: /etc/audit/auditd.conf
    line: disk_full_action = {{ var_auditd_disk_full_action.split('|')[0] }}
    regexp: ^\s*disk_full_action\s*=\s*.*$
    state: present
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-84045-4
  - DISA-STIG-RHEL-08-030060
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - auditd_data_disk_full_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }}
        mode: 0640
        path: /etc/audit/auditd.conf
        overwrite: true
OVAL test results details

disk error action  oval:ssg-test_auditd_data_disk_full_action:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/audit/auditd.confdisk_full_action = SUSPEND
Configure auditd mail_acct Action on Low Disk Spacexccdf_org.ssgproject.content_rule_auditd_data_retention_action_mail_acct mediumCCE-80678-6

Configure auditd mail_acct Action on Low Disk Space

Rule IDxccdf_org.ssgproject.content_rule_auditd_data_retention_action_mail_acct
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_data_retention_action_mail_acct:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80678-6

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01
cui3.3.1
hipaa164.312(a)(2)(ii)
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1
nerc-cipCIP-003-8 R1.3, CIP-003-8 R3, CIP-003-8 R3.1, CIP-003-8 R3.2, CIP-003-8 R3.3, CIP-003-8 R5.1.1, CIP-003-8 R5.3, CIP-004-6 R2.2.3, CIP-004-6 R2.3, CIP-007-3 R5.1, CIP-007-3 R5.1.2, CIP-007-3 R5.2, CIP-007-3 R5.3.1, CIP-007-3 R5.3.2, CIP-007-3 R5.3.3
nistIA-5(1), AU-5(a), AU-5(2), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.7.a
os-srgSRG-OS-000046-GPOS-00022, SRG-OS-000343-GPOS-00134
cis5.2.2.4
stigidRHEL-08-030020
stigrefSV-230388r1017196_rule
Description
The auditd service can be configured to send email to a designated account in certain situations. Add or correct the following line in /etc/audit/auditd.conf to ensure that administrators are notified via email for those situations:
action_mail_acct = root
Rationale
Email sent to the root account is typically aliased to the administrators of the system, who can take appropriate action.
OVAL test results details

email account for actions  oval:ssg-test_auditd_data_retention_action_mail_acct:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/audit/auditd.confaction_mail_acct = root
Configure auditd space_left Action on Low Disk Spacexccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_action mediumCCE-80684-4

Configure auditd space_left Action on Low Disk Space

Rule IDxccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_action
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_data_retention_space_left_action:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-80684-4

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01
cui3.3.1
hipaa164.312(a)(2)(ii)
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1
nistAU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.7
os-srgSRG-OS-000343-GPOS-00134
cis5.2.2.4
pcidss410.5.1, 10.5
stigidRHEL-08-030731
stigrefSV-244543r971542_rule
Description
The auditd service can be configured to take an action when disk space starts to run low. Edit the file /etc/audit/auditd.conf. Modify the following line, substituting ACTION appropriately:
space_left_action = ACTION
Possible values for ACTION are described in the auditd.conf man page. These include:
  • syslog
  • email
  • exec
  • suspend
  • single
  • halt
Set this to email (instead of the default, which is suspend) as it is more likely to get prompt attention. Acceptable values also include suspend, single, and halt.
Rationale
Notifying administrators of an impending disk space problem may allow them to take corrective action prior to any disruption.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

var_auditd_space_left_action='email'


var_auditd_space_left_action="$(echo $var_auditd_space_left_action | cut -d \| -f 1)"
#
# If space_left_action present in /etc/audit/auditd.conf, change value
# to var_auditd_space_left_action, else
# add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf
#

AUDITCONFIG=/etc/audit/auditd.conf

# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^space_left_action")

# shellcheck disable=SC2059
printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_space_left_action"

# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 -i -e "^space_left_action\\>" "$AUDITCONFIG"; then
    escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
    LC_ALL=C sed -i --follow-symlinks "s/^space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG"
else
    if [[ -s "$AUDITCONFIG" ]] && [[ -n "$(tail -c 1 -- "$AUDITCONFIG" || true)" ]]; then
        LC_ALL=C sed -i --follow-symlinks '$a'\\ "$AUDITCONFIG"
    fi
    cce="CCE-80684-4"
    printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "$AUDITCONFIG" >> "$AUDITCONFIG"
    printf '%s\n' "$formatted_output" >> "$AUDITCONFIG"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80684-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030731
  - NIST-800-171-3.3.1
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.7
  - PCI-DSSv4-10.5
  - PCI-DSSv4-10.5.1
  - auditd_data_retention_space_left_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_auditd_space_left_action # promote to variable
  set_fact:
    var_auditd_space_left_action: !!str email
  tags:
    - always

- name: Configure auditd space_left Action on Low Disk Space
  ansible.builtin.lineinfile:
    dest: /etc/audit/auditd.conf
    line: space_left_action = {{ var_auditd_space_left_action.split('|')[0] }}
    regexp: ^\s*space_left_action\s*=\s*.*$
    state: present
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-80684-4
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030731
  - NIST-800-171-3.3.1
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.7
  - PCI-DSSv4-10.5
  - PCI-DSSv4-10.5.1
  - auditd_data_retention_space_left_action
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }}
        mode: 0640
        path: /etc/audit/auditd.conf
        overwrite: true
OVAL test results details

space left action  oval:ssg-test_auditd_data_retention_space_left_action:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/audit/auditd.confspace_left_action = SYSLOG
Configure auditd space_left on Low Disk Spacexccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_percentage mediumCCE-86055-1

Configure auditd space_left on Low Disk Space

Rule IDxccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_percentage
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_data_retention_space_left_percentage:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-86055-1

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8
cobit5APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01
isa-62443-20094.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2
iso27001-2013A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1
nistAU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a)
nist-csfDE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4
pcidssReq-10.7
os-srgSRG-OS-000343-GPOS-00134
stigidRHEL-08-030730
stigrefSV-230483r971542_rule
Description
The auditd service can be configured to take an action when disk space is running low but prior to running out of space completely. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting PERCENTAGE appropriately:
space_left = PERCENTAGE%
Set this value to at least 25 to cause the system to notify the user of an issue.
Rationale
Notifying administrators of an impending disk space problem may allow them to take corrective action prior to any disruption.

# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

var_auditd_space_left_percentage='25'


grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \
  sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left_percentage%/g" /etc/audit/auditd.conf || \
  echo "space_left = $var_auditd_space_left_percentage%" >> /etc/audit/auditd.conf

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-86055-1
  - DISA-STIG-RHEL-08-030730
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.7
  - auditd_data_retention_space_left_percentage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_auditd_space_left_percentage # promote to variable
  set_fact:
    var_auditd_space_left_percentage: !!str 25
  tags:
    - always

- name: Configure auditd space_left on Low Disk Space
  ansible.builtin.lineinfile:
    dest: /etc/audit/auditd.conf
    line: space_left = {{ var_auditd_space_left_percentage }}%
    regexp: ^\s*space_left\s*=\s*.*$
    state: present
    create: true
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-86055-1
  - DISA-STIG-RHEL-08-030730
  - NIST-800-53-AU-5(1)
  - NIST-800-53-AU-5(2)
  - NIST-800-53-AU-5(4)
  - NIST-800-53-AU-5(b)
  - NIST-800-53-CM-6(a)
  - PCI-DSS-Req-10.7
  - auditd_data_retention_space_left_percentage
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
OVAL test results details

admin space left action   oval:ssg-test_auditd_data_retention_space_left_percentage:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_auditd_data_retention_space_left_percentage:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/audit/auditd.conf^[\s]*space_left[\s]+=[\s]+(\d+)%[\s]*$1
Include Local Events in Audit Logsxccdf_org.ssgproject.content_rule_auditd_local_events mediumCCE-82233-8

Include Local Events in Audit Logs

Rule IDxccdf_org.ssgproject.content_rule_auditd_local_events
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_local_events:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-82233-8

References:
nistCM-6
os-srgSRG-OS-000062-GPOS-00031, SRG-OS-000480-GPOS-00227
stigidRHEL-08-030061
stigrefSV-230393r1017200_rule
Description
To configure Audit daemon to include local events in Audit logs, set local_events to yes in /etc/audit/auditd.conf. This is the default setting.
Rationale
If option local_events isn't set to yes only events from network will be aggregated.
OVAL test results details

tests the value of local_events setting in the /etc/audit/auditd.conf file  oval:ssg-test_auditd_local_events:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/audit/auditd.conflocal_events = yes
Resolve information before writing to audit logsxccdf_org.ssgproject.content_rule_auditd_log_format lowCCE-82201-5

Resolve information before writing to audit logs

Rule IDxccdf_org.ssgproject.content_rule_auditd_log_format
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_log_format:def:1
Time2025-11-18T17:07:35-05:00
Severitylow
Identifiers:

CCE-82201-5

References:
nistCM-6, AU-3
osppFAU_GEN.1.2
os-srgSRG-OS-000255-GPOS-00096, SRG-OS-000480-GPOS-00227
app-srg-ctrSRG-APP-000096-CTR-000175, SRG-APP-000097-CTR-000180, SRG-APP-000098-CTR-000185, SRG-APP-000099-CTR-000190, SRG-APP-000100-CTR-000195, SRG-APP-000100-CTR-000200, SRG-APP-000109-CTR-000215, SRG-APP-000290-CTR-000670, SRG-APP-000357-CTR-000800
stigidRHEL-08-030063
stigrefSV-230395r1017201_rule
Description
To configure Audit daemon to resolve all uid, gid, syscall, architecture, and socket address information before writing the events to disk, set log_format to ENRICHED in /etc/audit/auditd.conf.
Rationale
If option log_format isn't set to ENRICHED, the audit records will be stored in a format exactly as the kernel sends them.
OVAL test results details

tests the value of log_format setting in the /etc/audit/auditd.conf file  oval:ssg-test_auditd_log_format:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/audit/auditd.conflog_format = ENRICHED
Set type of computer node name logging in audit logsxccdf_org.ssgproject.content_rule_auditd_name_format mediumCCE-82897-0

Set type of computer node name logging in audit logs

Rule IDxccdf_org.ssgproject.content_rule_auditd_name_format
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_name_format:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-82897-0

References:
nistCM-6, AU-3
osppFAU_GEN.1.2
os-srgSRG-OS-000039-GPOS-00017, SRG-OS-000342-GPOS-00133, SRG-OS-000479-GPOS-00224
pcidss410.2.2, 10.2
stigidRHEL-08-030062
stigrefSV-230394r958754_rule
Description
To configure Audit daemon to use a unique identifier as computer node name in the audit events, set name_format to hostname|fqd|numeric in /etc/audit/auditd.conf.
Rationale
If option name_format is left at its default value of none, audit events from different computers may be hard to distinguish.
Warnings
warning  Whenever the variable
var_auditd_name_format
uses a multiple value option, for example
A|B|C
, the first value will be used when remediating this rule.

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
# Remediation is applicable only in certain platforms
if rpm --quiet -q audit && rpm --quiet -q kernel; then

var_auditd_name_format='hostname|fqd|numeric'


var_auditd_name_format="$(echo $var_auditd_name_format | cut -d \| -f 1)"

if [ -e "/etc/audit/auditd.conf" ] ; then
    
    LC_ALL=C sed -i "/^\s*name_format\s*=\s*/Id" "/etc/audit/auditd.conf"
else
    touch "/etc/audit/auditd.conf"
fi
# make sure file has newline at the end
sed -i -e '$a\' "/etc/audit/auditd.conf"

cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak"
# Insert at the end of the file
printf '%s\n' "name_format = $var_auditd_name_format" >> "/etc/audit/auditd.conf"
# Clean up after ourselves.
rm "/etc/audit/auditd.conf.bak"

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:low
Disruption:low
Reboot:false
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-82897-0
  - DISA-STIG-RHEL-08-030062
  - NIST-800-53-AU-3
  - NIST-800-53-CM-6
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.2
  - auditd_name_format
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
- name: XCCDF Value var_auditd_name_format # promote to variable
  set_fact:
    var_auditd_name_format: !!str hostname|fqd|numeric
  tags:
    - always

- name: Set type of computer node name logging in audit logs - Define Value to Be
    Used in the Remediation
  ansible.builtin.set_fact: auditd_name_format_split="{{ var_auditd_name_format.split('|')[0]
    }}"
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82897-0
  - DISA-STIG-RHEL-08-030062
  - NIST-800-53-AU-3
  - NIST-800-53-CM-6
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.2
  - auditd_name_format
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

- name: Set type of computer node name logging in audit logs
  block:

  - name: Check for duplicate values
    ansible.builtin.lineinfile:
      path: /etc/audit/auditd.conf
      create: true
      regexp: (?i)(?i)^\s*name_format\s*=\s*
      state: absent
    check_mode: true
    changed_when: false
    register: dupes

  - name: Deduplicate values from /etc/audit/auditd.conf
    ansible.builtin.lineinfile:
      path: /etc/audit/auditd.conf
      create: true
      regexp: (?i)(?i)^\s*name_format\s*=\s*
      state: absent
    when: dupes.found is defined and dupes.found > 1

  - name: Insert correct line to /etc/audit/auditd.conf
    ansible.builtin.lineinfile:
      path: /etc/audit/auditd.conf
      create: true
      regexp: (?i)(?i)^\s*name_format\s*=\s*
      line: name_format = {{ auditd_name_format_split }}
      state: present
  when:
  - '"audit" in ansible_facts.packages'
  - '"kernel" in ansible_facts.packages'
  tags:
  - CCE-82897-0
  - DISA-STIG-RHEL-08-030062
  - NIST-800-53-AU-3
  - NIST-800-53-CM-6
  - PCI-DSSv4-10.2
  - PCI-DSSv4-10.2.2
  - auditd_name_format
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy

Complexity:low
Disruption:low
Reboot:true
Strategy:restrict
---
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
  config:
    ignition:
      version: 3.1.0
    storage:
      files:
      - contents:
          source: data:,{{ %23%0A%23%20This%20file%20controls%20the%20configuration%20of%20the%20audit%20daemon%0A%23%0A%0Alocal_events%20%3D%20yes%0Awrite_logs%20%3D%20yes%0Alog_file%20%3D%20/var/log/audit/audit.log%0Alog_group%20%3D%20root%0Alog_format%20%3D%20ENRICHED%0Aflush%20%3D%20%7B%7B.var_auditd_flush%7D%7D%0Afreq%20%3D%2050%0Amax_log_file%20%3D%20%7B%7B.var_auditd_max_log_file%7D%7D%0Anum_logs%20%3D%20%7B%7B.var_auditd_num_logs%7D%7D%0Apriority_boost%20%3D%204%0Aname_format%20%3D%20hostname%0A%23%23name%20%3D%20mydomain%0Amax_log_file_action%20%3D%20%7B%7B.var_auditd_max_log_file_action%7D%7D%0Aspace_left%20%3D%20%7B%7B.var_auditd_space_left%7D%7D%0Aspace_left_action%20%3D%20%7B%7B.var_auditd_space_left_action%7D%7D%0Averify_email%20%3D%20yes%0Aaction_mail_acct%20%3D%20%7B%7B.var_auditd_action_mail_acct%7D%7D%0Aadmin_space_left%20%3D%2050%0Aadmin_space_left_action%20%3D%20syslog%0Adisk_full_action%20%3D%20%7B%7B.var_auditd_disk_full_action%7D%7D%0Adisk_error_action%20%3D%20%7B%7B.var_auditd_disk_error_action%7D%7D%0Ause_libwrap%20%3D%20yes%0A%23%23tcp_listen_port%20%3D%2060%0Atcp_listen_queue%20%3D%205%0Atcp_max_per_addr%20%3D%201%0A%23%23tcp_client_ports%20%3D%201024-65535%0Atcp_client_max_idle%20%3D%200%0Atransport%20%3D%20TCP%0Akrb5_principal%20%3D%20auditd%0A%23%23krb5_key_file%20%3D%20/etc/audit/audit.key%0Adistribute_network%20%3D%20no%0Aq_depth%20%3D%20400%0Aoverflow_action%20%3D%20syslog%0Amax_restarts%20%3D%2010%0Aplugin_dir%20%3D%20/etc/audit/plugins.d }}
        mode: 0640
        path: /etc/audit/auditd.conf
        overwrite: true
OVAL test results details

tests the value of name_format setting in the /etc/audit/auditd.conf file  oval:ssg-test_auditd_name_format:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/audit/auditd.confname_format = NONE
Appropriate Action Must be Setup When the Internal Audit Event Queue is Fullxccdf_org.ssgproject.content_rule_auditd_overflow_action mediumCCE-85889-4

Appropriate Action Must be Setup When the Internal Audit Event Queue is Full

Rule IDxccdf_org.ssgproject.content_rule_auditd_overflow_action
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-auditd_overflow_action:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-85889-4

References:
nistAU-4(1)
os-srgSRG-OS-000342-GPOS-00133, SRG-OS-000479-GPOS-00224
stigidRHEL-08-030700
stigrefSV-230480r958754_rule
Description
The audit system should have an action setup in the event the internal event queue becomes full. To setup an overflow action edit /etc/audit/auditd.conf. Set overflow_action to one of the following values: syslog, single, halt.
Rationale
The audit system should have an action setup in the event the internal event queue becomes full so that no data is lost.
OVAL test results details

tests the value of overflow_action setting in the /etc/audit/auditd.conf file  oval:ssg-test_auditd_overflow_action:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/audit/auditd.confoverflow_action = SYSLOG
Verify Permissions on /etc/audit/auditd.confxccdf_org.ssgproject.content_rule_file_permissions_etc_audit_auditd mediumCCE-85871-2

Verify Permissions on /etc/audit/auditd.conf

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_etc_audit_auditd
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_etc_audit_auditd:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-85871-2

References:
nistAU-12(b)
os-srgSRG-OS-000063-GPOS-00032
stigidRHEL-08-030610
stigrefSV-230471r1069296_rule
Description
To properly set the permissions of /etc/audit/auditd.conf, run the command:
$ sudo chmod 0640 /etc/audit/auditd.conf
Rationale
Without the capability to restrict the roles and individuals that can select which events are audited, unauthorized personnel may be able to prevent the auditing of critical events. Misconfigured audits may degrade the system's performance by overwhelming the audit log. Misconfigured audits may also make it more difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one.
OVAL test results details

Testing mode of /etc/audit/auditd.conf  oval:ssg-test_file_permissions_etc_audit_auditd_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_etc_audit_auditd_0:obj:1 of type file_object
FilepathFilterFilter
/etc/audit/auditd.confoval:ssg-exclude_symlinks__etc_audit_auditd:ste:1oval:ssg-state_file_permissions_etc_audit_auditd_0_mode_0640or_stricter_:ste:1
Verify Permissions on /etc/audit/rules.d/*.rulesxccdf_org.ssgproject.content_rule_file_permissions_etc_audit_rulesd mediumCCE-85875-3

Verify Permissions on /etc/audit/rules.d/*.rules

Rule IDxccdf_org.ssgproject.content_rule_file_permissions_etc_audit_rulesd
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-file_permissions_etc_audit_rulesd:def:1
Time2025-11-18T17:07:35-05:00
Severitymedium
Identifiers:

CCE-85875-3

References:
nistAU-12(b)
os-srgSRG-OS-000063-GPOS-00032
stigidRHEL-08-030610
stigrefSV-230471r1069296_rule
Description
To properly set the permissions of /etc/audit/rules.d/*.rules, run the command:
$ sudo chmod 0600 /etc/audit/rules.d/*.rules
Rationale
Without the capability to restrict the roles and individuals that can select which events are audited, unauthorized personnel may be able to prevent the auditing of critical events. Misconfigured audits may degrade the system's performance by overwhelming the audit log. Misconfigured audits may also make it more difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one.
OVAL test results details

Testing mode of /etc/audit/rules.d/  oval:ssg-test_file_permissions_etc_audit_rulesd_0:tst:1  true

No items have been found conforming to the following objects:
Object oval:ssg-object_file_permissions_etc_audit_rulesd_0:obj:1 of type file_object
PathFilenameFilterFilter
/etc/audit/rules.d^.*rules$oval:ssg-exclude_symlinks__etc_audit_rulesd:ste:1oval:ssg-state_file_permissions_etc_audit_rulesd_0_mode_0600or_stricter_:ste:1
Ensure the audit Subsystem is Installedxccdf_org.ssgproject.content_rule_package_audit_installed mediumCCE-81043-2

Ensure the audit Subsystem is Installed

Rule IDxccdf_org.ssgproject.content_rule_package_audit_installed
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-package_audit_installed:def:1
Time2025-11-18T17:07:29-05:00
Severitymedium
Identifiers:

CCE-81043-2

References:
hipaa164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(C), 164.310(a)(2)(iv), 164.310(d)(2)(iii), 164.312(b)
nerc-cipCIP-004-6 R3.3, CIP-007-3 R6.5
nistAC-7(a), AU-7(1), AU-7(2), AU-14, AU-12(2), AU-2(a), CM-6(a)
osppFAU_GEN.1
pcidssReq-10.1
os-srgSRG-OS-000062-GPOS-00031, SRG-OS-000037-GPOS-00015, SRG-OS-000038-GPOS-00016, SRG-OS-000039-GPOS-00017, SRG-OS-000040-GPOS-00018, SRG-OS-000041-GPOS-00019, SRG-OS-000042-GPOS-00021, SRG-OS-000051-GPOS-00024, SRG-OS-000054-GPOS-00025, SRG-OS-000122-GPOS-00063, SRG-OS-000254-GPOS-00095, SRG-OS-000255-GPOS-00096, SRG-OS-000337-GPOS-00129, SRG-OS-000348-GPOS-00136, SRG-OS-000349-GPOS-00137, SRG-OS-000350-GPOS-00138, SRG-OS-000351-GPOS-00139, SRG-OS-000352-GPOS-00140, SRG-OS-000353-GPOS-00141, SRG-OS-000354-GPOS-00142, SRG-OS-000358-GPOS-00145, SRG-OS-000365-GPOS-00152, SRG-OS-000392-GPOS-00172, SRG-OS-000475-GPOS-00220
anssiR33, R73
cis5.2.1.1
pcidss410.2.1, 10.2
stigidRHEL-08-030180
stigrefSV-230411r1017217_rule
Description
The audit package should be installed.
Rationale
The auditd service is an access monitoring and accounting daemon, watching system calls to audit any access, in comparison with potential local access control policy such as SELinux policy.
OVAL test results details

package audit is installed  oval:ssg-test_package_audit_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedauditx86_64(none)1.el83.1.20:3.1.2-1.el8199e2f91fd431d51audit-0:3.1.2-1.el8.x86_64
Enable auditd Servicexccdf_org.ssgproject.content_rule_service_auditd_enabled mediumCCE-80872-5

Enable auditd Service

Rule IDxccdf_org.ssgproject.content_rule_service_auditd_enabled
Result
pass
Multi-check ruleno
OVAL Definition IDoval:ssg-service_auditd_enabled:def:1
Time2025-11-18T17:07:34-05:00
Severitymedium
Identifiers:

CCE-80872-5

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.3.1, 3.3.2, 3.3.6
hipaa164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(C), 164.310(a)(2)(iv), 164.310(d)(2)(iii), 164.312(b)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nerc-cipCIP-004-6 R3.3, CIP-007-3 R6.5
nistAC-2(g), AU-3, AU-10, AU-2(d), AU-12(c), AU-14(1), AC-6(9), CM-6(a), SI-4(23)
nist-csfDE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
osppFAU_GEN.1
pcidssReq-10.1
os-srgSRG-OS-000062-GPOS-00031, SRG-OS-000037-GPOS-00015, SRG-OS-000038-GPOS-00016, SRG-OS-000039-GPOS-00017, SRG-OS-000040-GPOS-00018, SRG-OS-000041-GPOS-00019, SRG-OS-000042-GPOS-00021, SRG-OS-000051-GPOS-00024, SRG-OS-000054-GPOS-00025, SRG-OS-000122-GPOS-00063, SRG-OS-000254-GPOS-00095, SRG-OS-000255-GPOS-00096, SRG-OS-000337-GPOS-00129, SRG-OS-000348-GPOS-00136, SRG-OS-000349-GPOS-00137, SRG-OS-000350-GPOS-00138, SRG-OS-000351-GPOS-00139, SRG-OS-000352-GPOS-00140, SRG-OS-000353-GPOS-00141, SRG-OS-000354-GPOS-00142, SRG-OS-000358-GPOS-00145, SRG-OS-000365-GPOS-00152, SRG-OS-000392-GPOS-00172, SRG-OS-000475-GPOS-00220
app-srg-ctrSRG-APP-000095-CTR-000170, SRG-APP-000409-CTR-000990, SRG-APP-000508-CTR-001300, SRG-APP-000510-CTR-001310
anssiR33, R73
cis5.2.1.4
pcidss410.2.1, 10.2
stigidRHEL-08-030181
stigrefSV-244542r1017348_rule
Description
The auditd service is an essential userspace component of the Linux Auditing System, as it is responsible for writing audit records to disk. The auditd service can be enabled with the following command:
$ sudo systemctl enable auditd.service
Rationale
Without establishing what type of events occurred, it would be difficult to establish, correlate, and investigate the events leading up to an outage or attack. Ensuring the auditd service is active ensures audit records generated by the kernel are appropriately recorded.

Additionally, a properly configured audit subsystem ensures that actions of individual system users can be uniquely traced to those users so they can be held accountable for their actions.
OVAL test results details

package audit is installed  oval:ssg-test_service_auditd_package_audit_installed:tst:1  true

Following items have been found on the system:
Result of item-state comparisonNameArchEpochReleaseVersionEvrSignature keyidExtended name
not evaluatedauditx86_64(none)1.el83.1.20:3.1.2-1.el8199e2f91fd431d51audit-0:3.1.2-1.el8.x86_64

Test that the auditd service is running  oval:ssg-test_service_running_auditd:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitPropertyValue
trueauditd.serviceActiveStateactive

systemd test  oval:ssg-test_multi_user_wants_auditd:tst:1  true

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
truemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service

systemd test  oval:ssg-test_multi_user_wants_auditd_socket:tst:1  false

Following items have been found on the system:
Result of item-state comparisonUnitDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependencyDependency
falsemulti-user.targetbasic.targetsysinit.targetdev-hugepages.mountsystemd-tmpfiles-setup.servicelvm2-lvmpolld.socketsystemd-update-utmp.servicesystemd-ask-password-console.pathsystemd-tmpfiles-setup-dev.servicedev-mqueue.mountlocal-fs.targethome.mount-.mountboot.mountsystemd-remount-fs.serviceostree-remount.servicesystemd-firstboot.servicesystemd-journald.servicesystemd-udev-trigger.servicesystemd-update-done.servicesystemd-random-seed.servicesys-fs-fuse-connections.mountmultipathd.servicenis-domainname.serviceimport-state.servicesystemd-pstore.servicesystemd-journal-catalog-update.servicecryptsetup.targetiscsi-onboot.serviceswap.targetdev-mapper-rhel\x2dswap.swapdracut-shutdown.servicesystemd-modules-load.servicesystemd-hwdb-update.serviceproc-sys-fs-binfmt_misc.automountloadmodules.servicesystemd-udevd.servicelvm2-monitor.serviceldconfig.servicesystemd-journal-flush.servicekmod-static-nodes.servicesystemd-sysctl.servicesys-kernel-config.mountsystemd-sysusers.serviceplymouth-read-write.serviceselinux-autorelabel-mark.servicesys-kernel-debug.mountplymouth-start.servicesystemd-machine-id-commit.servicesystemd-binfmt.servicepaths.targetmicrocode.serviceslices.target-.slicesystem.slicesockets.targetsystemd-initctl.socketvirtlogd.socketdm-event.socketlibvirtd.socketrpcbind.socketvirtlockd.socketcups.socketsystemd-coredump.socketsystemd-journald-dev-log.socketiscsiuio.socketlibvirtd-ro.socketiscsid.socketsystemd-udevd-control.socketavahi-daemon.socketsystemd-journald.socketsystemd-udevd-kernel.socketmultipathd.socketdbus.socketsssd-kcm.sockettimers.targetdnf-makecache.timersystemd-tmpfiles-clean.timerunbound-anchor.timermlocate-updatedb.timersystemd-user-sessions.servicesssd.serviceksmtuned.servicerpcbind.servicetuned.servicersyslog.servicesystemd-update-utmp-runlevel.serviceksm.servicekdump.servicerhsmcertd.servicevmtoolsd.servicesystemd-logind.serviceplymouth-quit-wait.serviceirqbalance.servicecrond.servicelibvirtd.servicecups.pathmdmonitor.servicedbus.serviceatd.serviceauditd.servicefirewalld.serviceavahi-daemon.servicesystemd-ask-password-wall.pathsshd.serviceinsights-client-boot.servicemcelog.servicevdo.servicegetty.targetgetty@tty1.serviceModemManager.servicechronyd.serviceNetworkManager.servicesmartd.servicecups.servicelibstoragemgmt.serviceremote-fs.targetiscsi.servicenfs-client.targetrpc-statd-notify.serviceremote-fs-pre.targetauth-rpcgss-module.servicevar-lib-machines.mountplymouth-quit.service
Enable Auditing for Processes Which Start Prior to the Audit Daemonxccdf_org.ssgproject.content_rule_grub2_audit_argument lowCCE-80825-3

Enable Auditing for Processes Which Start Prior to the Audit Daemon

Rule IDxccdf_org.ssgproject.content_rule_grub2_audit_argument
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_audit_argument:def:1
Time2025-11-18T17:07:34-05:00
Severitylow
Identifiers:

CCE-80825-3

References:
cis-csc1, 11, 12, 13, 14, 15, 16, 19, 3, 4, 5, 6, 7, 8
cjis5.4.1.1
cobit5APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.02, DSS05.03, DSS05.04, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01
cui3.3.1
hipaa164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(C), 164.310(a)(2)(iv), 164.310(d)(2)(iii), 164.312(b)
isa-62443-20094.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4
isa-62443-2013SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 7.1, SR 7.6
iso27001-2013A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2
nistAC-17(1), AU-14(1), AU-10, CM-6(a), IR-5(1)
nist-csfDE.AE-3, DE.AE-5, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4
osppFAU_GEN.1
pcidssReq-10.3
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000473-GPOS-00218, SRG-OS-000254-GPOS-00095
cis5.2.1.2
pcidss410.7.2, 10.7
stigidRHEL-08-030601
stigrefSV-230468r1017260_rule
Description
To ensure all processes can be audited, even those which start prior to the audit daemon, add the argument audit=1 to the default GRUB 2 command line for the Linux operating system. To ensure that audit=1 is added as a kernel command line argument to newly installed kernels, add audit=1 to the default Grub2 command line for Linux operating systems. Modify the line within /etc/default/grub as shown below:
GRUB_CMDLINE_LINUX="... audit=1 ..."
Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --args="audit=1"
Rationale
Each process on the system carries an "auditable" flag which indicates whether its activities can be audited. Although auditd takes care of enabling this for all processes which launch after it does, adding the kernel argument ensures it is set for every process during boot.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q grub2-common; }; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "audit" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"audit=[^\"]*\"(.*]\s*)/\1\"audit=1\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"audit=1\"]" >> "$KARGS_DIR/10-audit.toml"
    fi
else

    grubby --update-kernel=ALL --args=audit=1 --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80825-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030601
  - NIST-800-171-3.3.1
  - NIST-800-53-AC-17(1)
  - NIST-800-53-AU-10
  - NIST-800-53-AU-14(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IR-5(1)
  - PCI-DSS-Req-10.3
  - PCI-DSSv4-10.7
  - PCI-DSSv4-10.7.2
  - grub2_audit_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="audit=1"
  when:
  - '"kernel" in ansible_facts.packages'
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - CCE-80825-3
  - CJIS-5.4.1.1
  - DISA-STIG-RHEL-08-030601
  - NIST-800-171-3.3.1
  - NIST-800-53-AC-17(1)
  - NIST-800-53-AU-10
  - NIST-800-53-AU-14(1)
  - NIST-800-53-CM-6(a)
  - NIST-800-53-IR-5(1)
  - PCI-DSS-Req-10.3
  - PCI-DSSv4-10.7
  - PCI-DSSv4-10.7.2
  - grub2_audit_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "audit=1"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader audit=1
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of audit=1. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_audit_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_audit_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters audit=1 in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_audit_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters audit=1 in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_audit_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_audit_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for audit=1 in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_audit_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for audit=1 in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_audit_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_audit_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Extend Audit Backlog Limit for the Audit Daemonxccdf_org.ssgproject.content_rule_grub2_audit_backlog_limit_argument lowCCE-80943-4

Extend Audit Backlog Limit for the Audit Daemon

Rule IDxccdf_org.ssgproject.content_rule_grub2_audit_backlog_limit_argument
Result
fail
Multi-check ruleno
OVAL Definition IDoval:ssg-grub2_audit_backlog_limit_argument:def:1
Time2025-11-18T17:07:34-05:00
Severitylow
Identifiers:

CCE-80943-4

References:
nistCM-6(a)
osppFAU_STG.1, FAU_STG.3
os-srgSRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000062-GPOS-00031, SRG-OS-000254-GPOS-00095, SRG-OS-000341-GPOS-00132, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215
cis5.2.1.3
pcidss410.7.2, 10.7
stigidRHEL-08-030602
stigrefSV-230469r958752_rule
Description
To improve the kernel capacity to queue all log events, even those which occurred prior to the audit daemon, add the argument audit_backlog_limit=8192 to the default GRUB 2 command line for the Linux operating system. To ensure that audit_backlog_limit=8192 is added as a kernel command line argument to newly installed kernels, add audit_backlog_limit=8192 to the default Grub2 command line for Linux operating systems. Modify the line within /etc/default/grub as shown below:
GRUB_CMDLINE_LINUX="... audit_backlog_limit=8192 ..."
Run the following command to update command line for already installed kernels:
# grubby --update-kernel=ALL --args="audit_backlog_limit=8192"
Rationale
audit_backlog_limit sets the queue length for audit events awaiting transfer to the audit daemon. Until the audit daemon is up and running, all log messages are stored in this queue. If the queue is overrun during boot process, the action defined by audit failure flag is taken.

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel && { rpm --quiet -q grub2-common; }; then

if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then
    KARGS_DIR="/usr/lib/bootc/kargs.d/"
    if grep -q -E "audit_backlog_limit" "$KARGS_DIR/*.toml" ; then
        sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"audit_backlog_limit=[^\"]*\"(.*]\s*)/\1\"audit_backlog_limit=8192\"\2/" "$KARGS_DIR/*.toml"
    else
        echo "kargs = [\"audit_backlog_limit=8192\"]" >> "$KARGS_DIR/10-audit_backlog_limit.toml"
    fi
else

    grubby --update-kernel=ALL --args=audit_backlog_limit=8192 --env=/boot/grub2/grubenv

fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CCE-80943-4
  - DISA-STIG-RHEL-08-030602
  - NIST-800-53-CM-6(a)
  - PCI-DSSv4-10.7
  - PCI-DSSv4-10.7.2
  - grub2_audit_backlog_limit_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

- name: Update grub defaults and the bootloader menu
  ansible.builtin.command: /sbin/grubby --update-kernel=ALL --args="audit_backlog_limit=8192"
  when:
  - '"kernel" in ansible_facts.packages'
  - '"grub2-common" in ansible_facts.packages'
  tags:
  - CCE-80943-4
  - DISA-STIG-RHEL-08-030602
  - NIST-800-53-CM-6(a)
  - PCI-DSSv4-10.7
  - PCI-DSSv4-10.7.2
  - grub2_audit_backlog_limit_argument
  - low_disruption
  - low_severity
  - medium_complexity
  - reboot_required
  - restrict_strategy

[customizations.kernel]
append = "audit_backlog_limit=8192"

Complexity:medium
Disruption:low
Reboot:true
Strategy:restrict

bootloader audit_backlog_limit=8192
OVAL test results details

check all /boot/loader/entries/*.conf for expanded entries of audit_backlog_limit=8192. Leave out rescue boot entries. Accept also references to $kernelopts.  oval:ssg-test_grub2_audit_backlog_limit_entries_expanded_or_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check all /boot/loader/entries/*.conf files if there is at least one entry referencing $kernelopts. Leave out rescue entries.  oval:ssg-test_grub2_audit_backlog_limit_at_least_one_entry_referenced:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/boot/loader/entries/a3303742af9f4e2db46126a560ba64ef-4.18.0-553.el8_10.x86_64.confoptions $kernelopts $tuned_params

check for kernel command line parameters audit_backlog_limit=8192 in /boot/grub2/grubenv for all kernels  oval:ssg-test_grub2_audit_backlog_limit_argument_grub_env:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/boot/grub2/grubenvkernelopts=root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

check for kernel command line parameters audit_backlog_limit=8192 in /boot/efi/EFI/redhat/grubenv for all kernels  oval:ssg-test_grub2_audit_backlog_limit_argument_grub_env_uefi:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_audit_backlog_limit_argument_grub_env_uefi:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/boot/efi/EFI/redhat/grubenv^kernelopts=(.*)$1

check for audit_backlog_limit=8192 in /etc/default/grub via GRUB_CMDLINE_LINUX  oval:ssg-test_grub2_audit_backlog_limit_argument:tst:1  false

Following items have been found on the system:
Result of item-state comparisonPathContent
false/etc/default/grubGRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"

check for audit_backlog_limit=8192 in /etc/default/grub via GRUB_CMDLINE_LINUX_DEFAULT  oval:ssg-test_grub2_audit_backlog_limit_argument_default:tst:1  false

No items have been found conforming to the following objects:
Object oval:ssg-object_grub2_audit_backlog_limit_argument_default:obj:1 of type textfilecontent54_object
FilepathPatternInstance
/etc/default/grub^\s*GRUB_CMDLINE_LINUX_DEFAULT="(.*)"$1

Check for GRUB_DISABLE_RECOVERY=true in /etc/default/grub  oval:ssg-test_bootloader_disable_recovery_set_to_true:tst:1  true

Following items have been found on the system:
Result of item-state comparisonPathContent
true/etc/default/grubGRUB_DISABLE_RECOVERY="true"
Scroll back to the first rule
Red Hat and Red Hat Enterprise Linux are either registered trademarks or trademarks of Red Hat, Inc. in the United States and other countries. All other names are registered trademarks or trademarks of their respective companies.